I am confuse for lateinit and nullable variable, which one to use for variable.
lateinit var c: String
var d: String? = null
c = "UserDefinedTarget"
// if not added initialisation for c than throws UninitializedPropertyAccessException
if (c == "UserDefinedTarget") {
//do some stuff.
}
//not throws any exception whether d is initialise or not.
if(d == "UserDefinedTarget") {
//do some stuff
}
A type that is nullable is just that, a thing that has a valid state that is null.
A non-nullable late init var represents something where null is an invalid state, but for some reason you can't populate it in the constructor.
Android Activities are a good example of a use of lateinit. Activities must have a no args constructor and their lifecycle only really starts with onCreate().
These are two completely different concepts.
You can use lateinit to avoid null checks when referencing the property. It's very convenient in case your properties are initialized through dependency injection, or, for example, in the setup method of a unit test.
However, you should keep in mind that accessing a lateinit property before it has been initialized throws an exception. That means you should use them only if you are absolutely sure, they will be initialized.
Nullable types, on the other hand, are used when a variable can hold null.
class A {
lateinit var a: String
fun cat() {
print(a.length) // UninitializedPropertyAccessException is thrown
a = "cat"
print(a.length) // >>> 3
}
}
class B {
var b: String? = null
fun dog() {
print(b.length) // won't compile, null check is obligatory here
print(b?.length) // >>> null
b = "dog"
print(b?.length) // >>> 3
}
}
For more information:
Late-initialized properties
Nullable types
It depends.
Nullable variable means that variable can hold value or null. lateinit means that variable must be initialised later. It should be initialized before accessing it. If you attempt accessing uninitialized lateinit variable UninitializedPropertyAccessException will be thrown.
It's always better to avoid using nulls in your app. Nulls are evil. So if you can initialize variable in onCreate then it's better to use lateinit. Also if you use dependency injection in your app and fields should be injected it's also a valid case to use lateinit instead of handling nulls.
If for some reason you can't initialize variable, initializing code can result to null, or null can be assigned to this variable later than you should use nullable variable. Generally speaking if null is a valid value for the variable.
Use lateinit for properties that cannot be initialized in a constructor.
Related
I have a variable in my fragment class:
private lateinit var dManager: DataManager
And I'm initializing it before the first using here:
override fun onResume() {
super.onResume()
dManager = MyApp.gManager.getDataManager(sp,level,test)
if (dManager.hp< 1) {
...
...
...
}
}
This code works ok for me and most users (99.5%), but sometimes i get crash report
lateinit property dManager has not been initialized
How can this happen? What should I do to prevent it?
lateinit var makes compiler aware that’s not null
If your property is lifecycle-driven (e.g. a reference to a TextView
or ImageView which gets inflated during Android Activity lifecycle)
or it is initialized through injection, you cannot supply a non-null
initializer and you must declare its type as nullable. This in turn
will require you to use null checks every time you reference the
property, which may be a bit inconvenient, especially if you are
absolutely sure the property will get initialized at some point,
before you access it for the first time.
Kotlin has a simple solution for such a scenario, allowing you to mark the property with the lateinit modifier.
If you access the property before initialization, you’ll get
an UninitializedPropertyAccessException.
getDataManager(sp,level,test) may return sometimes null so for safe sides your solution would be like as :-
override fun onResume() {
super.onResume()
dManager = MyApp.gManager.getDataManager(sp,level,test)
if (::dbManager.isInitialized && dManager.hp< 1) {
...
...
...
}
}
May be your getDataManager(sp,level,test) return null value
OR
As per the document you have to check object with .isInitialized property.
Returns true if this lateinit property has been assigned a value, and false otherwise.
Check lateinit var is initialized
lateinit var file: File
if (::file.isInitialized) { ... }
I have the following code in PHP public static $bitValueTable = null; and I want to convert it into Kotlin. My variable is a null array in the first step but I add some value after the program runs.
how can I convert?
By default any variable in kotlin can't hold null values but still you can create nullable object using ? operator, for better understanding check below url https://kotlinlang.org/docs/reference/null-safety.html.
So to create nullable array use below syntax
var myTypeArray: Array<type>? = null // check below example
var myStrArray: Array<String>? = null
Kotlin Arrays Documentation
Thanks
In kotlin you can use nullable objects with the secure call operator "?".
Now, you have a static variable in PHP, in kotlin there is no "static" as such, however the companion object {} fulfills the same function.
In this way the equivalent of public static $bitValueTable = null;
in kotlin is:
companion object {
var bitValueTable : Array<Type>? = null
}
Obs:
The default value of the variables in kotlin is public
Lets assume, there is a non-null enum MainFlag object ( like enum class MainFlag { PARAM1, PARAM2 }) in an Activity by lateinit var:
private lateinit var flag: MainFlag
Furtheron, I get that later in the onCreate() like:
flag = intent?.extras?.getSerializable(ARG_FLAG) as MainFlag
and I used to use this flag in that activity on several places with null-checks
flag?.let{
//..
}
but then Android Studio complains:
Unnecessary safe call on a non-null receiver of type
MainActivity.Companion.MainFlag
I am not sure using it without null checks, because if intent?.extras? fails, flag will not be set, thus null?
yes it can be null. You are getting the warning because you are down casting to a not nullable type
intent?.extras?.getSerializable(ARG_FLAG) as MainFlag
should be
intent?.extras?.getSerializable(ARG_FLAG) as? MainFlag
First of all you have to declare the variable as
private lateinit var flag: MainFlag?
or
private var flag: MainFlag? = null
then you can set the value of flag like this
flag = intent?.extras?.getSerializable(ARG_FLAG) as? MainFlag
Now the flag variable is nullable. So you need to check for nullable everywhere in the code like this:
flag?.let{
//..
}
If you are sure that intent?.extras?.getSerializable(ARG_FLAG) will never be null (and also none of the intermediate objects: intent and intent.extras), then you can keep everything as is and just use flag.let { ... instead of flag?.let { ....
If there is a chance that any of those is null, then you have to use a MainFlag?-type for your flag, e.g.:
private var flag : MainFlag? = null // having null in place basically makes the lateinit meaningless and rightfully lateinit does not allow nullable types
flag = intent?.extras?.getSerializable(ARG_FLAG) as? MainFlag
... or risk a TypeCastException when keeping as MainFlag.
The reason why Android Studio complains about the unnecessary safe call, is, that you specified the type as non-nullable MainFlag (indirectly via your as MainFlag-cast). So if you access flag it must already be there and it must be not-null (you said it so). The code however will fail at the cast, if there was a null-value involved, i.e. the cast to the non-nullable type could not succeed.
class Foo : ViewModel() {
val bars: MutableLiveData<ArrayList<Bar>> = MutableLiveData()
get() {
if(field.value == null) {
field.setValue(ArrayList()) // NullPointerException
}
}
}
class FooTest(){
#Test fun itShouldNotBlowUp() {
Foo() //nullPointerException
}
}
I don't understand how to initialize the value of a MutableLiveData object. I've tried to lazily initialize it via the getter and with an init block. Both approaches throw a null pointer when setting the value. bars is not null however.
Here is the stacktrace:
java.lang.NullPointerException
at android.arch.core.executor.DefaultTaskExecutor.isMainThread(DefaultTaskExecutor.java:58)
at android.arch.core.executor.ArchTaskExecutor.isMainThread(ArchTaskExecutor.java:116)
at android.arch.lifecycle.LiveData.assertMainThread(LiveData.java:434)
at android.arch.lifecycle.LiveData.setValue(LiveData.java:279)
at android.arch.lifecycle.MutableLiveData.setValue(MutableLiveData.java:33)
at org.Foo.<init>(Foo.kt:10)
at org.FooTest.ShouldNotBlowUp(FooTest.kt:3)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
How do I initialize the ArrayList and set it as the value of bars?
Maybe the reason is that you are trying to run this in the test. Your sample fails in DefaultTaskExecutor.isMainThread(), that looks like this:
public boolean isMainThread() {
return Looper.getMainLooper().getThread() == Thread.currentThread();
}
So Looper.getMainLooper() returns null in the test environment.
And also, have you tried to initialize property via 'lazy' delegate?
val bars: MutableLiveData<List<Bar>> by lazy {
MutableLiveData<List<Bar>>().apply {
value = emptyList()
}
}
That works fine in my case and feels more idiomatic
The answer to the question is: if you want to give it an empty value clearly, but the variable is not null and you can at least use it without breaking the program.
to create a variable:
val myVariable = MutableLiveData<ArrayList<String>>()
Well, now you only have to pass the constructor of the empty list within the type parentheses so that your variable does not remain with a Null value.
val myVariable = MutableLiveData<ArrayList<String>>(arrayListOf())
I know it's an old question but in case it can help someone, it helped me discover this ;)
If a variable is nullable in Kotlin, we need to either do a safety call ?., or !!. for explicitly call.
When I was trying to use some extensions(such as run or let) from nullable variable , I noticed that .run is fine and IDE did not complain it, usually I will receive a warning to remind me it is not a safety call.
Does it make any difference for ?.run{} and .run{} in kotlin? Is it considered as null safety if I use .run{} ?
var a? = "..."
a?.run{}
a.run{}
You'll need to safely handle the null somewhere.
Either when accessing a:
a?.run { }
Or when accessing this inside run on a:
a.run { this?.toSomething() }
Using a String? as an example, these both print null and the compiler is ok with both:
val x: String? = null
println(x.run { this?.toUpperCase() }) // prints null, type is String?
println(x?.run { this.toUpperCase() }) // prints null, type is String?