When initializing a val of type Sensor by lazy the compiler ignores that the value returned can be null
Note: The val is declared as Sensor and not Sensor?
private val sensorManager by lazy { getSystemService(Context.SENSOR_SERVICE) as SensorManager }
private val proximitySensor: Sensor by lazy { sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY) }
sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY) can also return null and the method is not annotated with #Nullable So, it can't be checked at compile-time.
Also, the Android Studio gives a warning when checking for null as we have not declared it as Sensor?
Shouldn't it raise a compile time error when assigning a nullable to a val which is non-null?
Resolved
When the value proximitySensor is accessed and if it is null, it produces a runtime exception as expected.
'java.lang.String android.hardware.Sensor.getName()' on a null object reference
lazy() in the Kotlin standard library reference as follows:
lazy() returns a Lazy<T> instance that stored lambda initializer.
The first call of getter executes a lambda passed to lazy() and
stores its result.
Subsequently, the getter execution returns the stored value.
Shouldn't an exception be thrown when the assignment is made and not
when we try to access it?
operator fun setValue(
thisRef: Any?,
property: KProperty<*>, value: String
) {
// assign
// An exception should be generated at this point when the assignment happens
}
Should there be a Compile-time error?
Shouldn't it raise a compile time error when assigning a nullable to a val which is non-null?
Yes, but that's not what's happening here In this case, you are assigning a platform type to your value and telling the compiler to treat it as non-nullable. There should be a runtime error of IllegalStateException-as #MarkoTopolnik points out, this is a bug.
Bug
This bug is covered by KT-8135. Here's a linked issue directly related to delegates, KT-24258.
There's also a discussion topic with some runnable examples here.
When will the runtime exception be thrown?
The error will be triggered when you use proximitySensorin a way that requires a non-nullable value such as the following:
val s: Sensor = proximitySensor
proximitySensor.someMethod()
println(proximitySensor.someProperty)
But this will not throw an exception when the property is initialized! We can fix that...
Why isn't there an error when the Lazy property is first initialized?
Since sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY) is implemented in java it returns a platform type. Therefore, the Lazy delegate is of type, Lazy<Sensor!>.
The Lazy class runs the initializer lambda. The result of the lambda is saved and then returned as type T, which is Sensor!.
Solution
If we explicitly declare the type params when calling the lazy function:
private val proximitySensor: Sensor by lazy<Sensor> { sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY) }
Then an exception will be thrown as soon as the lazy value is accessed.
Other options
Declare proximitySensor as nullable
The warning is steering you in the right direction either don't check for null or declare it as nullable. Tell the compiler the value should be treated as a nullable by explicitly declaring the type as nullable:
private val proximitySensor: Sensor? by lazy { sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY) }
Provide a default value inside the lazy initializer
As you mentioned, sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY) may return null. We could return a default value:
val proximitySensor: Sensor by lazy {
sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY) ?: SOME_DEFAULT_VALUE
}
A note about Platform Types
Platform types in Kotlin make working with Java more pragmatic by allowing you, the caller, to decide if the type is nullable or non-nullable. The alternative of assuming everything in Java is nullable would mean you would have to do a lot of painful null checking even though the Java code never returns null.
*Note: If you're writing Java code, you can use the #Nullable and #NonNull annotations to provide the metadata to the Kotlin compiler as well as other static analysis tools. Libraries such as the Spring Framework have done this to their APIs.
Here's a talk by Andrey Breslav, a Kotlin developer, that speaks in depth about this design decision of platform types for interoperability.
Thanks to your question this is now a confirmed bug in Kotlin compiler's JVM backend.
Earlier Content of this Answer:
I made an MCVE with your problem:
val sensorManager = SensorManager()
val proximitySensor: Sensor by lazy { sensorManager.sensor }
fun main(args: Array<String>) {
println(proximitySensor)
}
This is the support Java code:
public class SensorManager {
public Sensor getSensor() {
return null;
}
}
public class Sensor {}
And... guess what? It prints null. I call "Kotlin bug" :)
You ask:
Shouldn't an exception be thrown when the assignment is made and not when we try to access it?
In the case of a lazy delegate, the only assignment happens when you access the property for the first time. This propagates to the SynchronizedLazyImpl.value getter, which contains the well-known double-checked lazy init idiom, and it is the only time the initializer block you supply runs.
Even without that detailed discussion it should be clear that the essence of the lazy delegate is to postpone initialization until the latest possible time, which is the first property access. A correct implementation would ensure an exception is thrown before the evaluation of the non-nullable property completes. The current implementation is, unfortunately, not correct.
Related
In Kotlin, if you don't want to initialize a class property inside the constructor or in the top of the class body, you have basically these two options (from the language reference):
Lazy Initialization
lazy() is a function that takes a lambda and returns an instance of Lazy<T> which can serve as a delegate for implementing a lazy property: the first call to get() executes the lambda passed to lazy() and remembers the result, subsequent calls to get() simply return the remembered result.
Example
public class Hello {
val myLazyString: String by lazy { "Hello" }
}
So, the first call and the subsequential calls, wherever it is, to myLazyString will return Hello
Late Initialization
Normally, properties declared as having a non-null type must be initialized in the constructor. However, fairly often this is not convenient. For example, properties can be initialized through dependency injection, or in the setup method of a unit test. In this case, you cannot supply a non-null initializer in the constructor, but you still want to avoid null checks when referencing the property inside the body of a class.
To handle this case, you can mark the property with the lateinit modifier:
public class MyTest {
lateinit var subject: TestSubject
#SetUp fun setup() { subject = TestSubject() }
#Test fun test() { subject.method() }
}
The modifier can only be used on var properties declared inside the body of a class (not in the primary constructor), and only when the property does not have a custom getter or setter. The type of the property must be non-null, and it must not be a primitive type.
So, how to choose correctly between these two options, since both of them can solve the same problem?
Here are the significant differences between lateinit var and by lazy { ... } delegated property:
lazy { ... } delegate can only be used for val properties, whereas lateinit can only be applied to vars, because it can't be compiled to a final field, thus no immutability can be guaranteed;
lateinit var has a backing field which stores the value, and by lazy { ... } creates a delegate object in which the value is stored once calculated, stores the reference to the delegate instance in the class object and generates the getter for the property that works with the delegate instance. So if you need the backing field present in the class, use lateinit;
In addition to vals, lateinit cannot be used for nullable properties or Java primitive types (this is because of null used for uninitialized value);
lateinit var can be initialized from anywhere the object is seen from, e.g. from inside a framework code, and multiple initialization scenarios are possible for different objects of a single class. by lazy { ... }, in turn, defines the only initializer for the property, which can be altered only by overriding the property in a subclass. If you want your property to be initialized from outside in a way probably unknown beforehand, use lateinit.
Initialization by lazy { ... } is thread-safe by default and guarantees that the initializer is invoked at most once (but this can be altered by using another lazy overload). In the case of lateinit var, it's up to the user's code to initialize the property correctly in multi-threaded environments.
A Lazy instance can be saved, passed around and even used for multiple properties. On contrary, lateinit vars do not store any additional runtime state (only null in the field for uninitialized value).
If you hold a reference to an instance of Lazy, isInitialized() allows you to check whether it has already been initialized (and you can obtain such instance with reflection from a delegated property). To check whether a lateinit property has been initialized, you can use property::isInitialized since Kotlin 1.2.
A lambda passed to by lazy { ... } may capture references from the context where it is used into its closure.. It will then store the references and release them only once the property has been initialized. This may lead to object hierarchies, such as Android activities, not being released for too long (or ever, if the property remains accessible and is never accessed), so you should be careful about what you use inside the initializer lambda.
Also, there's another way not mentioned in the question: Delegates.notNull(), which is suitable for deferred initialization of non-null properties, including those of Java primitive types.
lateinit vs lazy
lateinit
i) Use it with mutable variable[var]
lateinit var name: String //Allowed
lateinit val name: String //Not Allowed
ii) Allowed with only non-nullable data types
lateinit var name: String //Allowed
lateinit var name: String? //Not Allowed
iii) It is a promise to compiler that the value will be initialized in future.
NOTE: If you try to access lateinit variable without initializing it then it throws UnInitializedPropertyAccessException.
lazy
i) Lazy initialization was designed to prevent unnecessary initialization of objects.
ii) Your property will not be initialized unless you use it.
iii) It is initialized only once. Next time when you use it, you get the value from cache memory.
iv) It is thread safe(It is initialized in the thread where it is used for the first time. Other threads use the same value stored in the cache).
v) The property can only be val.
vi) The property can be of any type (including primitives and nullables, which are not allowed with lateinit).
Very Short and concise Answer
lateinit: It initialize non-null properties lately
Unlike lazy initialization, lateinit allows the compiler to recognize that the value of the non-null property is not stored in the constructor stage to compile normally.
lazy Initialization
by lazy may be very useful when implementing read-only(val) properties that perform lazy-initialization in Kotlin.
by lazy { ... } performs its initializer where the defined property is first used, not its declaration.
Additionnally to hotkey's good answer, here is how I choose among the two in practice:
lateinit is for external initialisation: when you need external stuff to initialise your value by calling a method.
e.g. by calling:
private lateinit var value: MyClass
fun init(externalProperties: Any) {
value = somethingThatDependsOn(externalProperties)
}
While lazy is when it only uses dependencies internal to your object.
In addition to all of the great answers, there is a concept called lazy loading:
Lazy loading is a design pattern commonly used in computer programming to defer initialization of an object until the point at which it is needed.
Using it properly, you can reduce the loading time of your application. And Kotlin way of it's implementation is by lazy() which loads the needed value to your variable whenever it's needed.
But lateinit is used when you are sure a variable won't be null or empty and will be initialized before you use it -e.g. in onResume() method for android- and so you don't want to declare it as a nullable type.
Everything is correct above, but one of facts simple explanation LAZY----There are cases when you want to delay the creation of an instance of your object until its
first usage. This technique is known as lazy initialization or lazy instantiation. The main
purpose of lazy initialization is to boost performance and reduce your memory footprint. If
instantiating an instance of your type carries a large computational cost and the program
might end up not actually using it, you would want to delay or even avoid wasting CPU
cycles.
Diff btw lateinit and lazy
lateinit
Use only with mutable variable i.e. var and non-nullable data types
lateinit var name: String //Allowed with non-nullable
You are telling the compiler that the value will be initialized in future.
NOTE: If you try to access lateinit variable without initializing it then it throws UnInitializedPropertyAccessException.
lazy
Lazy initialization was designed to prevent unnecessary initialization of objects.
Your variable will not be initialized unless you use it.
It is initialized only once. Next time when you use it, you get the value from cache memory.
It is thread safe.
The variable can only be val and non-nullable.
Cheers :)
If you are using Spring container and you want to initialize non-nullable bean field, lateinit is better suited.
#Autowired
lateinit var myBean: MyBean
If you use an unchangable variable, then it is better to initialize with by lazy { ... } or val. In this case you can be sure that it will always be initialized when needed and at most 1 time.
If you want a non-null variable, that can change it's value, use lateinit var. In Android development you can later initialize it in such events like onCreate, onResume. Be aware, that if you call REST request and access this variable, it may lead to an exception UninitializedPropertyAccessException: lateinit property yourVariable has not been initialized, because the request can execute faster than that variable could initialize.
Why can't we use lateinit with nullable variables?
lateinit var v: String?
lateinit modifier is not allowed on properties of nullable types
lateinit is only for avoid null checks in future, that's why lateinit modifier is not allowed on properties of nullable types.
If you want it to be nullable then simply you can use like var b: String? = null
If you want to make a variable of nullable type then you don't need late init . The doc says
Normally, properties declared as having a non-null type must be initialized in the constructor.
However, fairly often this is not convenient. For example, properties can be initialized through
dependency injection, or in the setup method of a unit test. In this case, you cannot supply a nonnull
initializer in the constructor, but you still want to avoid null checks when referencing the
property inside the body of a class.
So late init is intended to be used when you intend to initialize a variable somewhere not in the constructor and also want to avoid null checks.
As stated in the documentation, lateinit specializes on non-nullable properties:
Normally, properties declared as having a non-null type must be initialized in the constructor. However, fairly often this is not convenient. For example, properties can be initialized through dependency injection, or in the setup method of a unit test. In this case, you cannot supply a non-null initializer in the constructor, but you still want to avoid null checks when referencing the property inside the body of a class.
Also, if you take a look at the byte code of such a lateinit property, you see that the compiler adds a code block to ensure that this property has been initialized when being accessed. For lateinit properties, null indicates the properties initial but invalid state.
class WithLateInit {
lateinit var something : String
}
becomes
public final class WithLateInit {
#NotNull
public String something;
#NotNull
public final String getSomething() {
String var10000 = this.something;
if (var10000 == null) { // <- here you can see the lateinit check
Intrinsics.throwUninitializedPropertyAccessException("something");
}
return var10000;
}
//setter
}
Kotlin's type system is aimed at eliminating the danger of null references from code
so both represent compromises. With lateinit you ensure that the variable will eventually be initialized to non-null. If you cannot even guarantee that, you can use nullable.
Normally, properties declared as having a non-null type must be
initialized in the constructor. However, fairly often this is not
convenient.
For example, properties can be initialized through dependency
injection, or in the setup method of a unit test. In this case, you
cannot supply a non-null initializer in the constructor, but you
still want to avoid null checks when referencing the property
inside the body of a class.
To handle this case, you can mark the property with the lateinit
modifier.
That's why it doesn't support null.
So, if you indicate any var as lateinit meaning compiler simply ignores it for initialization and mark it as non-null type that would be initialized in nearer future and is why it doesn't support nullable type to avoid runtime ambiguities.
One of the main feature in kotlin is Null Safe.
By default it won't allow u to create a null value. You have explicitly defined
var a: String? = null
, If u don't want to initialise any value to the variable there come "lateinit". While using lateinit variable You do pre-check whether it is initialised or not
You can declare a local variable without initializing or adding lateinit, but same is not true for global variable, my question is why ? why compiler do not give error for local variable too?
for example :
class A{
var abc : String // this is not allowed by compiler
fun myOwnedFun(){
var abcd : String // this is allowed
}
}
I understand one thing is allowed and the other is not but am curious as to why.
The scope of a local variable is the function where it's defined. The compiler has no problem verifying that a local variable has been initialized inside the function before it's accessed for the first time.
As for a public class field, the scope is infinite, hence the compiler has no way to ensure the field will be initialized before it's accessed. To prevent the program from getting into a bad state by using an uninitialized variable the compiler raises an error.
class A{
var abc : String // throws compile time error
fun myOwnedFun(){
var abcd : String // throws compile time error while accessing it
abcd. // throws error now, suggests you to initialize it.
}
}
This is because Kotlin is built as a null safe language, which means all the variables must be initialized before using it, either as nullable which should be suffixed with ? and assigned to null or with default constructor for any other classes, or use a lateinit var if you are sure that you will be assigning it somewhere before accessing it first.
In Android Kotlin, lateinit var is widely used for global variables which will be assigned in the lifecycle methods such as onCreate() etc.,
I have converted my old java model class to kotlin data class. Some of objects are annotated with #NonNull in java. My question is if null is passed from our backend in my data class what will happen? Does making this username nullable can help in preventing crash if null is passed?
Java code:
public abstract class Comment(){
#NonNull
public abstract String username();
}
Kotlin code:(what happen in this case if null is passed?)
data class Comment(val username: String)
Kotlin code:(it can handle null)
data class Comment(val username: String?)
In java - everything will compile and give a warning
In kotlin - your compiler won't let you pass null to nullable or #notnull annotated type
For example:
public static boolean isUserLoggedIn(#NotNull ctx: Context) {
return ...
}
// Kotlin Invocation
fun main(args: Array<String>) {
isUserLoggedIn(null)
}
And compilation error:
e: C:\_projects\test.kt: (37, 37): Null can not be a value of a non-null type Context
:app:compileDebugKotlin FAILED
FAILURE: Build failed with an exception.
In Java you are able to call this java-method with no compile error but your IDE should show warning (passing null to parameter annotated as #notnull).
Also, in Java you can pass null parameters to notnull kotlin methods. It'll compile and give a warning.
Kotlin supports some of annotations (like JetBrains, Android, Eclipse). The full list can be found here: https://kotlinlang.org/docs/reference/java-interop.html#nullability-annotations
Edit 1 - regarding the comment:
It depends if runtime null check is enabled or not. Kotlin, to ensure null safety in generated code adds kotlin.jvm.internal.Intrinsics.checkNotNull call.
See: https://github.com/JetBrains/kotlin/blob/master/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/Intrinsics.java
If value is null NPE will be thrown. So, NPE will be thrown every time null is passed. Every time, even if your code could handle null value.
But, you can disable this check. Then, your code will be small lighter, and also won't throw exception every time null is passed. But you will lose a lot of profits from null safety, and it's also shows that something is bad in your design.
See how: Disable not null checks in Kotlin
Kotlin type system tells a nullable type from a not-nullable type. A declaration like x: String? means null is allowed (same as it was in Java)
The declaration x: String means you do not accept nulls. Kotlin compiler takes care of it and it will try it's best to discard any incorrect code, that tries setting null there.
Kotlin compiler understands annotations like #Nullable or #NotNull: see the documentation for more details
https://kotlinlang.org/docs/reference/java-interop.html#nullability-annotations
Of course, there are ways to call a nun-nullable method with null value (e.g. via Java Reflection or just from another JVM language). To protect from that, Kotlin Compiler emits null checks automatically, and the code will fail-fast.
What's an idiomatic way to do code this in Kotlin?
private var someVar: SomeClass? = null
private fun getSomeVar(): SomeClass {
if (someVar == null) {
someVar = getDefaultSomeVar()
}
return someVar
}
Android Studio warns me about the return type. In Java this code is, however, proper and idiomatic. I mean, instead of changing the return type to SomeClass? maybe there's still a better way?
Actually, getSomeVar() can never return null
The compiler complains because, theoretically, a different thread could change someVar in between the assignment and the return statement.
The idiomatic solution here would be to use property delegation:
private val someVar: SomeClass by lazy { getDefaultSomeVar() }
This initializes the property when it is first accessed in a thread safe manner. Also note that it is now a non-nullable val, instead of a nullable var, which makes it generally easier to work with.
You do lose the ability to modify it later on. If it needs to be mutable you currently have to make that yourself. For an example implementation see this SO question: Kotlin lazy default property
The following two solutions take the method in the question (the 'java way') for granted and just show a way to prevent the compiler warning. However, in your situation these are not advised as they both have drawbacks over the lazy initialized property:
1) Introduce a local variable. This variable is safe from being mutated by other threads and allows the compiler to do a Smart Cast:
private fun getSomeVar(): SomeClass {
var value = someVar
if(value == null) {
value = getDefaultSomeVar()
someVar = value
}
return value
}
The method itself is however still not thread safe. In a multithreaded environment, getDefaultSomeVar() could be called multiple times and it is not guaranteed that the returned value of this method is equal to someVar.
2) Use !!: the double bang operator. This converts a nullable type to non-nullable. But now you lose the protection and null safety that the kotlin compiler enforces on you.
return someVar!!
As the documentation puts it: 'If you want an NPE, you can have it'
You can write:
return someVar!!
this will return a non-null value, however if it's null it will throw an NPE.
It could be shorter and without any warnings
private fun getSomeVar(): SomeClass {
return someVar?:getDefaultSomeVar()
}