Member lateinit variables initialization can be checked with:
class MyClass {
lateinit var foo: Any
...
fun doSomething() {
if (::foo.isInitialized) {
// Use foo
}
}
}
However this syntax doesn't work for local lateinit variables. Lint reports the error: "References to variables aren't supported yet". There should logically be a way to do that since lateinit variables are null internally when uninitialized.
Is there a way to check if local variables are initialized?
The code you show in your question is actually fine in Kotlin 1.2 and beyond, since foo is an instance variable, not a local variable.
The error message you report and mentioned in Alexey's comment (Unsupported [References to variables aren't supported yet]) can be triggered by a true local variable, for example in the doSomethingElse method below.
class MyClass {
lateinit var foo: Any
fun doSomething() {
if (::foo.isInitialized) { // this is fine to use in Kotlin 1.2+
// Use foo
}
}
fun doSomethingElse() {
lateinit var bar: Any
if (::bar.isInitialized) { // this is currently unsupported (see link in Alexey's comment.
// Use bar
}
}
}
So it seems like this is currently unsupported. The only place that comes to mind where a lateinit local would be used would be if the local is variable captured in a lambda?
Related
Is it possible to use Spring AOP (AspectJ) with Kotlin properties? Specifically due to how Kotlin compiles properties to Java:
a getter method, with the name calculated by prepending the get prefix
a setter method, with the name calculated by prepending the set prefix (only for var properties)
a private field, with the same name as the property name (only for properties with backing fields)
Consider the following minimal reproducible example:
#Retention(AnnotationRetention.RUNTIME)
#Target(AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION)
annotation class TestAnnotation
...
#Aspect
class TestAspect {
#Around("#annotation(annotation)")
fun throwingAround(joinPoint: ProceedingJoinPoint, annotation: TestAnnotation): Any? {
throw RuntimeException()
}
}
...
internal class MinimalReproducibleExample {
open class TestProperties {
#TestAnnotation
val sampleProperty: String = "sample property"
#TestAnnotation
fun sampleFunction(): String = "sample function"
}
private lateinit var testProperties: TestProperties
#BeforeEach
fun setUp() {
val aspectJProxyFactory = AspectJProxyFactory(TestProperties())
aspectJProxyFactory.addAspect(TestAspect())
val aopProxyFactory = DefaultAopProxyFactory()
val aopProxy = aopProxyFactory.createAopProxy(aspectJProxyFactory)
testProperties = aopProxy.proxy as TestProperties
}
#Test
fun test() {
println(testProperties.sampleProperty)
println(testProperties.sampleFunction())
}
}
Running the test yields:
null
sample function
When debugging I can see that the generated proxy is a cglib-backed proxy, which should be able to proxy to a concrete class, but it does not seem to invoke the configured aspect. Is there something wrong with my #Around definition, or is this a limitation of Kotlin properties and/or proxying concrete classes?
Was able to trigger the aspect above with the following changes:
Use a "site target" for the getter: #get:TestAnnotation
Make the property/function both open
My code saves an object to database in some bigger method, but I don't need to test this.
So I want to mock the Repository.save method. But the save method returns the saved object.
I tried the following:
#MockK
private lateinit var mockJobRepository: JobRepository
val jobSlot = slot<Job>()
// ...
every { mockJobRepository.save<Job>(capture(jobSlot)) }
returns(jobSlot.captured)
But it throws an runtime error:
"lateinit property captured has not been initialized"
How do I just return the given argument in the mock?
Have you tried
private val mockJobRepository = mockk<JobRepository>()
?
I've notice #Mockk annotations on lateinit vars can be finicky
When using annotations, you have to tell Mockk at some point to initialize the annotated properties. Assuming you're using JUnit 5, you can do it by initializing mocks in #BeforeEach:
class Test {
#MockK
private lateinit var emailService: EmailService
#BeforeEach
fun setUp() {
MockKAnnotations.init(this)
}
}
...or just use the Mockk-Extension for JUnit:
#ExtendWith(MockKExtension::class)
class Test {
#MockK
private lateinit var emailService: EmailService
}
Btw. less verbose option than capturing the argument would be returnsArgument:
every { mockJobRepository.save<Job>(any()) } returnsArgument 0
So what I want to achieve is that to have the top-level variable set some time later in the main function, but I don't want to make it a lateinit var which certainly breaks the Extension variable functionality.
For instance this code doesn't work since extension variables don't support lateinit modifier:
lateinit var Dispatchers.Konvironment: MainCoroutineDispatcher
private set
fun main() {
...
Dispatchers.Konvironment = ArbitraryMainDispatcher(Thread.currentThread()) { queue.add(it) }
...
}
So what I finally came up with is to use a dummy variable and implement the getter of the val variable.
val Dispatchers.Konvironment: MainCoroutineDispatcher
get() = dispatcher
private lateinit var dispatcher: MainCoroutineDispatcher
fun main() {
...
dispatcher = ArbitraryMainDispatcher(Thread.currentThread()) { queue.add(it) }
...
}
But it is certainly not clean way to do that. It looks ugly (ish) creating multiple variable in the top-level structure is not very clean architecture.
So is there any possible clean workarounds? Sort of like lazy initialization, by some delegates or something.
Well, partially answering your question:
var Dispatchers.Konvironment: MainCoroutineDispatcher
get() = dispatcher
private set(value) {
dispatcher = value
}
private lateinit var dispatcher: MainCoroutineDispatcher
fun main() {
...
Dispatchers.Konvironment = ArbitraryMainDispatcher(Thread.currentThread()) { queue.add(it) }
...
}
will give you the desired way of assigning the value. There is no way to get rid of this additional lazyinit variable, though.
Extensions are nothing more than just some Kotlin syntax sugar for static methods which take an instance of the extended class as one of the arguments, and perform some action. If you're familiar with Java then, for example, these extensions:
// Extensions.kt
fun Foo.extendedAction() {
println(this)
}
var Foo.extendedBar: Bar
get() = this.bar
set(value) {
this.bar = value
}
are under the hood these methods in Java:
public class ExtensionsKt {
public static final void extendedAction(Foo foo) {
System.out.println(foo);
}
public static final Bar getExtendedBar(Foo foo) {
return foo.getBar();
}
public static final Bar setExtendedBar(Foo foo, Bar bar) {
foo.setBar(bar);
}
}
The conclusion which maybe drawn from the above is that extensions don't actually add anything to the extended classes' signatures, they simply decorate them with additional functionality. Or, as put in the docs:
Extensions do not actually modify classes they extend. By defining an extension, you do not insert new members into a class, but merely make new functions callable with the dot-notation on variables of this type.
So you can see, unless dispatcher somehow already exists within Dispatchers, you can't do what you want without providing an external, "backing" variable which value can be actually referenced by the extension.
In the section: Properties and Fields of the reference of kotlin, the following examples is written:
var allByDefault: Int? // error: explicit initializer required, default getter and setter implied
However, I test the code and there is no error in compiling and running.
Here is my code"
fun main(args:Array<String>){
var allByDefault:Int?
}
So why does the documentation write:
error: explicit initializer required, default getter and setter implied
I have searched google for help but haven't found any result which can help me.
#toniedzwiedz 's answer has solved the issue. It's my fault. I mistook property and variable.
fun main(args:Array<String>){
var allByDefault:Int?
}
What you have here is a var local to the main method, not a property.
class MyClass {
//this is a property of MyClass that requires some means of initialization
var allByDefault: Int? // Error: Property must be initialized or be abstract
fun foo() {
var local: Int? // this is a local variable defined in the scope of foo, which is fine
// ...
}
}
I wonder if there is a way to check if a lateinit variable has been initialized. For example:
class Foo() {
private lateinit var myFile: File
fun bar(path: String?) {
path?.let { myFile = File(it) }
}
fun bar2() {
myFile.whateverMethod()
// May crash since I don't know whether myFile has been initialized
}
}
There is a lateinit improvement in Kotlin 1.2 that allows to check the initialization state of lateinit variable directly:
lateinit var file: File
if (this::file.isInitialized) { ... }
See the annoucement on JetBrains blog or the KEEP proposal.
UPDATE: Kotlin 1.2 has been released. You can find lateinit enhancements here:
Checking whether a lateinit var is initialized
Lateinit top-level properties and local variables
Using .isInitialized property one can check initialization state of a lateinit variable.
if (::file.isInitialized) {
// File is initialized
} else {
// File is not initialized
}
You can easily do this by:
::variableName.isInitialized
or
this::variableName.isInitialized
But if you are inside a listener or inner class, do this:
this#OuterClassName::variableName.isInitialized
Note: The above statements work fine if you are writing them in the same file(same class or inner class) where the variable is declared but this will not work if you want to check the variable of other class (which could be a superclass or any other class which is instantiated), for ex:
class Test {
lateinit var str:String
}
And to check if str is initialized:
What we are doing here: checking isInitialized for field str of Test class in Test2 class.
And we get an error backing field of var is not accessible at this point.
Check a question already raised about this.
Try to use it and you will receive a UninitializedPropertyAccessException if it is not initialized.
lateinit is specifically for cases where fields are initialized after construction, but before actual use (a model which most injection frameworks use).
If this is not your use case lateinit might not be the right choice.
EDIT: Based on what you want to do something like this would work better:
val chosenFile = SimpleObjectProperty<File?>
val button: Button
// Disables the button if chosenFile.get() is null
button.disableProperty.bind(chosenFile.isNull())
If you have a lateinit property in one class and need to check if it is initialized from another class
if(foo::file.isInitialized) // this wouldn't work
The workaround I have found is to create a function to check if the property is initialized and then you can call that function from any other class.
Example:
class Foo() {
private lateinit var myFile: File
fun isFileInitialised() = ::file.isInitialized
}
// in another class
class Bar() {
val foo = Foo()
if(foo.isFileInitialised()) // this should work
}
This will work
if (::list.isInitialized) {
//true
}
else {
//false
}
Accepted answer gives me a compiler error in Kotlin 1.3+, I had to explicitly mention the this keyword before ::. Below is the working code.
lateinit var file: File
if (this::file.isInitialized) {
// file is not null
}
Checking lateinit var
To check whether a lateinit var was initialized or not, simply use an .isInitialized boolean on the property reference :: .
if (foo::bar.isInitialized) {
println(foo.bar)
}
Playground Kotlin's code may look like this:
fun main() {
var declarative = Declarative()
declarative.checkLateInit()
}
class Declarative {
lateinit var compose: String
fun checkLateInit() {
println(this::compose.isInitialized)
compose = "Jetpack Compose 1.2"
if (this::compose.isInitialized) {
println(this.compose)
}
}
}
// Result:
// false
// Jetpack Compose 1.2
This checking is only available for the properties that are accessible lexically, i.e. declared in the same type or in one of the outer types, or at top level in the same file.
kotlin.UninitializedPropertyAccessException: lateinit property clientKeypair has not been initialized
Bytecode says...blah blah..
public final static synthetic access$getClientKeypair$p(Lcom/takharsh/ecdh/MainActivity;)Ljava/security/KeyPair;
`L0
LINENUMBER 11 L0
ALOAD 0
GETFIELD com/takharsh/ecdh/MainActivity.clientKeypair : Ljava/security/KeyPair;
DUP
IFNONNULL L1
LDC "clientKeypair"
INVOKESTATIC kotlin/jvm/internal/Intrinsics.throwUninitializedPropertyAccessException (Ljava/lang/String;)V
L1
ARETURN
L2
LOCALVARIABLE $this Lcom/takharsh/ecdh/MainActivity; L0 L2 0
MAXSTACK = 2
MAXLOCALS = 1
Kotlin creates an extra local variable of same instance and check if it null or not, if null then throws 'throwUninitializedPropertyAccessException' else return the local object.
Above bytecode explained here
Solution
Since kotlin 1.2 it allows to check weather lateinit var has been initialized or not using .isInitialized