Is 'it' automatically smart cast internally? [duplicate] - android

This question already has answers here:
Smart cast to 'Type' is impossible, because 'variable' is a mutable property that could have been changed by this time
(12 answers)
Closed 1 year ago.
I am studying Android and I am also studying Kotlin.
While writing Android code, I was curious about using it in a let function.
MainActivity.kt
class MainActivity : AppCompatActivity() {
private var curFrag: Fragment? = null
curFrag = fm.primaryNavigationFragment
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// curFrag?.let { transaction.hide(curFrag) } // error.
curFrag?.let { transaction.hide(it) }
}
}
ERROR
Smart cast to 'Fragment' is impossible, because 'curFrag' is a mutable property that could have been changed by this time
In the lambda expression of let(), T is curFrag and the type is Fragment? is.
And T(curFrag) can be replaced by it.
But the moment I used curFrag instead of it, the IDE displayed an error message.
Later, when I checked the type of it, it was Fragment? It was not a Fragment type.
Honestly, I don't understand well.
I don't know why it is automatically smart cast and should only be used for immutable variables.

Kotlin is a null safe language, it tries to eliminate every possible null references from the code. You can perform a nullability check on the variable and then can use it like this
if(curfrag != null) { transaction.hide(curFrag)
This too will only work if variable curfrag is immutable (that means a local variable which is not modified between the check and the usage or a member val which has a backing field and is not overridable), because otherwise it might happen that curfrag changes to null after the check from some other thread.
But Safe calls ?. with let always gives us non nullable result, what Safe calls operator ?. does is, it only performs any operation following it, only if the variable is not-null otherwise it returns null.
It works with all mutable types or member var, It check for the null once and then provides the result. If value is non null it performs the defined operation otherwise skips it. it refers to the copy of that non-null value.
So when you do this
curFrag?.let { transaction.hide(curFrag) }
curFrag can be null as you are directly passing a nullable value.
But in this case
curFrag?.let { transaction.hide(it) }
it only passes value if it's a non-null value.

The let function basically creates a new variable with the same value as whatever you called it on, so it is not really smart-casting the original property.
If you use ?.let, let isn't even called if the value was null. The safe call means the receiver let is being called on is not a nullable value to begin with because otherwise let isn't called at all. The it inside let is just a reference to what it was called on.
Effectively, though it is conceptually similar to smart-casting. There is not really a way to write equivalent Kotlin code that does what ?.let is doing because the ?. safe call is a special operator that has no expanded form.

Related

What is the difference between LiveData<String>() and LiveData<String?>()

I am new to LiveData thing in general and I am having a hard time understanding the difference between LiveData<String>() and LiveData<String?>(). I used them interchangeably and nothing seams to break. I know that LiveData.getValue() is marked with #Nullable in Java, so we end up getting String? anyway. So what makes LiveData<String?>() different from LiveData<String>()?
This ended up a bit long but I hope it covers everything!
A LiveData is meant to be observed. The observer receives data, and the LiveData's type says what type that data is. A LiveData<String> will only supply non-null Strings to its observers. A LiveData<String?> can supply Strings and nulls.
Which of those you want depends on what you're doing! Do you need to supply nulls, e.g. for some kind of missing value or whatever? Should they be part of your data? If not, like in any other situation, avoid making the type nullable unless it needs to be.
When an observer first observes a LiveData, it receives the current value. That way it can immediately handle the current data, update to display the current state, etc. But it's possible for a LiveData to have no value initially:
// non-null
val liveDataWithValue = MutableLiveData<String>("hi")
val emptyLiveData = MutableLiveData<String>()
// nullable
val nullableLiveDataWithValue = MutableLiveData<String?>(null)
val emptyNullableLiveData = MutableLiveData<String?>()
The first one there has an initial value. If you observe it, and that value hasn't been updated, the observer will immediately be called with "hi" for its parameter.
The second one has no value. If you observe that, the observer won't be called until a value is set on it. This is useful when you don't actually have any initial data - you can still set up your observer, and nothing will happen until some data is actually pushed.
The third one is the same as the first - it's a nullable String? but with a value of null. That's still a value so if you observe it, the observer will immediately be called with that null. It's still a piece of data your observer has to react to and process.
The last one is nullable but with no initial value. Like the second one, this means there's nothing for the observer to receive at first - but when it does have a value set on it, it could be a null. null is just another kind of value!
But if you go poking around at the LiveData's value property, instead of interacting with it through observe, then that no value state is represented internally by null. Java (or at least the version Android targets) doesn't really have a representation of no value separate from null, so that's just how they have to do things. It just doesn't publish anything until you explicitly set a value on it.
So for each of these:
val emptyLiveData = MutableLiveData<String>()
val nullableLiveDataWithValue = MutableLiveData<String?>(null)
val emptyNullableLiveData = MutableLiveData<String?>()
if you read their value in this state, it will be null for all of them. One explicitly has a value of null set on it, the others are both empty. This also means that even though emptyLiveData's type is non-null, its value property can be null, just because of this "can be empty" situation which is true for all LiveData objects. The nullability of the type is purely about what gets passed to observers.
You generally shouldn't be reading value anyway, except internally (wherever you're actually setting the value). Everything else should be interacting with that LiveData by observing it and reacting to the values that are published, and those values will be whatever type (nullable or not) that you specified
LiveData<String?>() meens that livedata can store null, read please this article to be fully informed: https://kotlinlang.org/docs/null-safety.html

kotlin add method, having difficulty understanding the "it" argument

private fun defaultOptions() {
val options = ArrayList<TextView>()
tvoptionone?.let { options.add(0, it) }
}
I am currently using the add(index, element) method in kotlin, However, I don't seem to understand what it represents in the element parameter of the add method.
These are the parameters for the add method I am trying to use
add(index, element)
it is the context object on which you've used the let function.
As you've used it with safe call operator (?.) it would only call let if object is non null.
Using ?.let ensures the lambda to be executed only when the object is non null. ?. ensures that object has to be non null and let makes that object available as it inside the lamda.
Here
tvoptionone?.let { options.add(0, it) }
it is a TextView as tvoptionone is a TextView, and it has a value same as tvoptionone.
In the below code
tvoptionone?.let { options.add(0, it) } }
it refers to tvoptionone
notice that lambda passed to let will be called only when tvoptionone is not null, so here it refers to tvoptionone and its value is not null
Questionmark after variable tvoptionone indicates that this variable can be null. If you write just:
options.add(0, tvoptionone)
and variable happens to be null then the add method will throw an error with wording like param element cannot be null or so.
Keyword let, in this particular example, is kind of a guardian against passing null into add method. If tvoptionone has some value (is not null) then it will be tvoptionone itself. Otherwise add method will not be called at all and compilation error will be avoided.
Scope functions are the ones you run on a value, and provide a function to run using that value. There's two kinds of scope functions in Kotlin - the ones where the value is passed in as a parameter (let, also etc.) and the ones where it becomes this (run, apply etc). They work the same, it just changes how you interact with the value - sometimes one is more convenient or suitable than the other
it is just the default name for the parameter passed in:
// these two are the same thing
name.let { println("Hi $it") }
name.let { it -> println("Hi $it") }
// rename it to something that reads better if you like
personData.firstName.let { name -> println("Hi $name") }
People have mentioned the null-check feature, where you can make the let block only run if the value is non-null:
name?.let { println("Hi $it, looking very not-null today") }
but another use for it is creating a temporary variable. If you have a var, it's possible the value will change while you're using it, so it's common to take a copy of it (so you know your copy won't change):
var importantNumber = 123
if (importantNumber > 100) {
// but it might have just been changed by another thread / coroutine and be < 100!
doThing(importantNumber)
}
var importantNumber = 123
val copy = importantNumber
if (copy > 100) {
// the copy can't change, so we know once we've checked it, it's fine
doThing(copy)
}
since let creates a variable to pass in as a parameter, it's basically making a copy in the same way:
var importantNumber = 123
importantNumber.let { if (it > 100) doThing(it) } // safe because 'it' won't change
and that's super important for nullable vars - if you check they're null, you need to know they'll stay null. Using ?.let guarantees that the value passed in as it will be non-null, because it's a copy that's been null-checked

Why does ?. is inevitable when calling a method although instance isn't nullable?

I have to following variable declaration:
var baseItemList: MutableList<BaseDataItem>? = null
when writing the line:
baseDataItemsList?.get(position).getObjectTypeNum()
I'm getting an error saying that:
Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type BaseDataItem?
but, get method doesn't return a BaseDataItem?, only a BaseDataItem since the BaseDataItem inside the brackets is without a question mark.
Can someone explain me this error, and why i have to add this question mark?
Looking at this code:
baseDataItemsList?.get(position)?.getObjectTypeNum()
The call ?.get(position) returns the position if baseDataItemsList is not null, but otherwise returns null. So even though baseDataItemsList.get() would return a non-nullable BaseDataItem (only possible to call if baseDataItemsList is not nullable), the null-safe baseDataItemsList?.get() call returns a nullable BaseDataItem?, where the null condition indicates that baseDataItemsList is null. So you must use ?.getObjectTypeNum() to account for this.
Side note: in my opinion combining var with a mutable collection is often a code smell, because you're making something mutable in two different ways, which makes it more error-prone to work with.
Make use of Kotlins scope functions, for example the let scope to avoid that warning:
baseDataItemsList?.let { baseDataItemList ->
baseDataItemList.get(position).getObjectTypeNum()
}
That way you assert that baseDataItemList cannot be null inside the let scope. If you want to read more about that topic, take a look into the documentation

What is difference between !! and ? in kotlin's null safety? [duplicate]

This question already has answers here:
What's the difference between !! and ? in Kotlin?
(6 answers)
Closed 3 years ago.
I am a bit confused about the usage of ? and !! in the following instance.
lat = mLastLocation?.latitude.toString()
longi = mLastLocation!!.longitude.toString()
Which null-safety operator should I be using?
TL;DR:
?. operator is safe. Use it when you are unsure about the chain nullability.
!!. operator is meant to be used only when you are sure that the previous chain operation result is not null. Otherwise, crash.
if mLastLocation is never null, feel safe about using !!. (And about rethinking a little bit your code), otherwise, use ?.
Introduction
You have hit one of the best (and most useful) points while coding in Kotlin.
here which null safety operator I should use?
It depends on the behavior you want to achieve.
In Kotlin, you have to be very specific about what you want to do with null values, because the language is designed to be null-safe out of the box.
Of course, targeting the JVM brings many challenges to a programming language. The eventuality of having null values is one of these, and Kotlin, as we'll see, handles this in a really smart manner.
Purpose
We could explain the full theory behind those two operators, but I believe an example is really all you need.
Suppose you have a class, called Location, which we will declare in a nullable variable.
In Kotlin, this is represented as val location: Location?
Let's also say Location class has a property called lat, which is a nullable String, and a lon non-nullable String.
data class User(val lat: String?, val lon: String)
Operator ?.
Kotlin Safe Call Operator Docs
This operator is the safe call operator.
If you use it in a call chain, it is checking that your code chain goes onto the next element just if the previous element is not null. Otherwise, null is retuned from the statement.
val location: Location? = getLocation()
println(location.lat) // Compile-time error.
println(location?.lat) // Works fine.
This happens because in the first case, the object before ?. is nullable, thus the Kotlin Compiler infers that accessing a nullable property can lead to NPEs.
location could be null or not-null.We just don't know what it will be, and the Kotlin environment strictly makes sure that you are handling the eventuality of that value being null, as the type of our references variable is defined as nullable.
However, a certain variable being null is something you developer may not know. Sometimes it is not even up to you to receive a null or non-null value.
In this case you can safely stick with ?, knowing that this operator is your friend if you are unsure about whether what you're referencing will be null.
val location: Location = getSafeLocation()
val nullableLocation: Location? = getLocation()
// Fine, may print "null" or the value, if present.
// println accepts nullable values
println(location.lar)
// 100% sure it'll print the corrisponding String value
println(location.lon)
// May print "null", "null", or the lat String value.
// The first "null" is because the ? operator will check if
// nullableLocation is null. If it is, it stops the execution
// chain and returns null. Otherwise, it assumes nullableLocation is safe
// and goes on.
//
// The second "null" is because the value itself of lat
// is declared as String? and Kotlin knows it may be null.
// If println() did not accept null values, this call would fail,
// but instead it prints "null" in case lat is indeed null.
println(nullableLocation?.lat)
// Since lat was the last element of the chain, it is not
// delivered as the parameter type anymore, and thus if we
// want to read a property from it we have to ensure that it isn't null.
println(nullableLocation?.lat?.length)
// This, as you may guess, returns wither null in case nullableLocation is null,
// otherwise 100% sure it will print lon value, since it is not a String? but a String.
println(nullableLocation?.lon)
Operator !!.
Kotlin Double-Bang Operator Docs
This is the dreaded double-bang operator.
Talking about syntax, it is very similar to ?., since it is used in the same place.
To describe it in a really simple way: if anything before the call is null, your code will crash. Instantly. Without warnings.
With !!. you're explicitly saying that
Kotlin has to ignore any type nullability marker and to perform the operation you intend, even though it enters in a kind of danger zone.
This is known as a force, because you're forcing the Kotlin environment to believe that the previous statement is not null.
This operator best use case is when porting another library to Kotlin, or while handling API RESTful responses, situations where null values may come in, but because of environment/platform reasons, you know that some value can not be null. This helps you bringing type safety in the Kotlin world in the first place, if used properly.
But for mainstream software, this feature is meant for a really specific and narrow usage: If you are 100% sure that the previous call is not null, go ahead.
val location: Location? = getLocation()
println(location!!.lon)
The previous code may crash if location is
Which one to use
Both operators are type-transforming. They both turn nullable values into non-nullable ones. The way the do it is the changing factor.
As a general rule, if you're sure the value you are targeting is not null, use !!., otherwise stick with ?.
if you define a variable as
var myFirstVar:String? = null
this means that "myFirstVar" can have a null value and when you use "myFirstVar" you should indicate whether or not it has a null value
myFirstVar!!.toString
in here you're saying that you're 100% that myFirstVar will not be null (maybe you've given it a value before calling it)
but if you use ?
myFirstVar?.toString
you're indicating that myFirstVar might have a null value, the ? will chick if myFirstVar is null or not, if it is then it will not convert it to string (the application will not crash) and if it wasn't then it will convert it to string, it is a safety check to reduce the null crashes.
If a variable is declared as nullable type, you have two options when using it.
Let's take this for example:
private var myButton: Button? = null
So you have two option for the myButton. You can evaluate it, or keep it as it is. But the program on the run doesn't know what you have done with the variable before. So, in order to be safe Kotlin language provides you with ? and !! operators. One is for the safety of program, so it won't crash and cause a KNPE:
myButton?.setOnClickListener{
}
If the button is null, the app won't crash. Now, if you are 100% sure that you have evaluated the Button with a value different from null, you can use !!:
myButton!!.setOnClickListener{
}
In this case, if you run the program and the myButton is null, you will have a crash.
Null Safety Myth
However, this is not a null safety case (I guess). What people mean by null safety in Kotlin is exactly this:
private val myButton: Button = someButtonInitialization()
If you evaluate it to null, the compiler would yell at you because Button is a non nullable type. Otherwise it would be Button?.
This is null safety IMO, and not !! or ?.
Special case: You can have:
private lateinit var myButton: Button
If you never evaluate the myButton you will never have KNPE, but an UninitializedPropertyException which has nothing to do with Null threat or null safety.
Let's have an example
var a: String? = "Hello world!"
fun test1() {
a?.trim()
}
fun test2() {
a!!.trim()
}
The first decompiled function is:
public static final void test1() {
String var10000 = a;
if (var10000 != null) {
String var0 = var10000;
StringsKt.trim((CharSequence)var0).toString();
}
}
The second decompiled function is:
public static final void test2() {
String var10000 = a;
if (var10000 == null) {
Intrinsics.throwNpe();
}
String var0 = var10000;
StringsKt.trim((CharSequence)var0).toString();
}
Where Intrinsics.throwNpe(); is defined as:
public static void throwNpe() {
throw sanitizeStackTrace(new KotlinNullPointerException());
}
So a?.trim() will do nothing if var a was null
So a!!.trim() will throw an exception if var a was null

Nullable var with `?` vs. lateinit var

What is the best way to define global variables in a Kotlin/Android activity/fragment?
What are the different scenarios when you should use these 2 methods for defining a global variable:
var viewpager: CustomViewPager? = null
or
lateinit var viewpager: CustomViewPager
?
If I use the former, I won't have to check for null in my code. For example if I used lateinit for the following:
viewpager = activity?.findViewById<CustomViewPager>(R.id.viewpager) then I would have to check for null.
using lateinit, you are saying that you absolutely will make sure that an instance of that variable is created somewhere (otherwise, your application will throw an exception if a lateinit has not been initialized) and then that variable also will not be null throughout the rest of your project, compared to using null, it means that this object potentially could be null somewhere in your code for the rest of the project and you will have to deal with nullability throughout.
If you are positive that you are not going to make a variable null and you require an instance of it always, use lateinit
Ask yourself this question :
Am I 100% sure that I will be using an instance of this variable somewhere in this class ?
If the answer to that is Yes, you should probably be using lateinit, as lateinit forces you to create an instance of it.
If the answer is No, you should probably be using a nullable field instead.
Taken from here : https://www.kotlindevelopment.com/lateinit-kotlin/
The lateinit keyword stands for late initialization. Lateinit comes very handy when a non-null initializer cannot be supplied in the constructor, but the developer is certain that the variable will not be null when accessing it, thus avoiding null checks when referencing it later.
i would recommend using the first method, it's better cause it eliminates app crashes if the code is trying to access the viewPager while it was not initialized, you can also use this in order to access the viewPager in the first method to make sure your app won't crash
viewPager?.let{"it"
}
this will create an instance of the viewPager if it was already initialized and use it as a val instead of a var, the instance is called "it" and u can rename it as anything by doing this after the first {
viewPager?.let{viewPager ->
}
If you make sure that variable will never be null in any place of the code then use lateinit. Here you have to initialize it before using it (in your case you can initialize it inside onCreate(..). Make sure that the variable will never become null later ELSE a null pointer exception will be fired.
In this way, you can directly use the variable without checking it if it's null or not.
Also, you can detect if the variable has initialized or not using:
if (:: viewpager.isInitialized) { .... }
On the other hand, you should use the nullable option using ?
In this case, you should check the variable before using it if it's null or not.
you can use ?. for that. You also can combine that with let for example:
viewPager?.let {
....
}

Categories

Resources