What is the purpose of defining 2 variable in this tutorial? Why don't we use 1 variable instead and accessing view with it?
https://developer.android.com/topic/libraries/view-binding#usage
The getter on the second variable (binding) uses the !! operator to assert that the variable is non-null when accessed.
Essentially the backing field (_binding) is nullable in order to represent the state before onCreateView and after onDestroyView whereas the getter provides an easy way to access the field without scattering null checks or assertions elsewhere in your code.
Related
In my layout that is called activity_main.xml, I have two TextView. I can access it from my MainActivity by using a variable
binding = ActivityMainBinding.inflate(layoutInflater)
in this way:
binding.textView1
binding.textView2
In my code binding.textView1 returns an object TextView but the second returns an object TextView?.
That forces me to access the second TextView with ?. operator like
binding.textView2?.text = "HelloWorld"
because using the . operator cause the error
Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type TextView?
So the questions are there a method for defining a View not nullable in my layout? How is possible that two objects with the same property had different behavior?
The object was nullable because it was present not in all XML layouts, so in some layouts it was null.
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.
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
I created my class (exending Activity) with an attribute int a.
The attribute is automatically initialized to 0 in the method onCreate().
Is it normal?
This is normal. You see, "int" is a primitive type. It is not an object, and so, it cannot hold a "null" value. If you want your variable to be null at onCreate(), you must change its type to the Object representation of it. The "Integer" class represents the primitive type "int".
From Official Java Tutorial:
Fields that are declared but not initialized will be set to a
reasonable default by the compiler.
Check default values for each data type: https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html
in the method onCreate().
No, it's not initialized in onCreate(). It's initialized when object of your class is instantiated.
0
Yes it is. Variable of int is primitive type can only hold numeric values and unless you initialize it other way it will be assigned 0 (contrary to i.e. Integer which can also be null).
See docs:
https://docs.oracle.com/javase/tutorial/java/javaOO/index.html
https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html
One of the coolest features of the Android data binding support is that it also generates fields for View with IDs set. This tidies up the codebase as no field or findViewById() calls are necessary.
But the problem is that the binding instance can only be retrieved via the bind() call which tends to schedule binding. This is bad when the data is being received asynchronously and commonly the NullPointerException gets thrown.
Can the binding instance with View fields be retrieved minus the actual data binding process?
Stack-trace:
java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.Boolean.booleanValue()' on a null object reference
at com.app.android.databinding.ActivityRestaurantDetailsBinding.executeBindings(ActivityRestaurantDetailsBinding.java:381)
at android.databinding.ViewDataBinding.executePendingBindings(ViewDataBinding.java:350)
at android.databinding.ViewDataBinding$6.run(ViewDataBinding.java:167)
at android.databinding.ViewDataBinding$5.onViewAttachedToWindow(ViewDataBinding.java:137)
at android.view.View.dispatchAttachedToWindow(View.java:14525)
This doesn't seem to make sense, data binding will ignore null variables thus no null pointer should be thrown, that is, i believe, one of its most promoted features. If you need to modify variables after async calls etc you can just use dataBinding.executePendingBindings()
From the docs
The generated binding class will have a setter and getter for each of the described variables. The variables will take the default Java values until the setter is called — null for reference types, 0 for int, false for boolean, etc.
and
Generated data binding code automatically checks for nulls and avoid null pointer exceptions. For example, in the expression #{user.name}, if user is null, user.name will be assigned its default value (null). If you were referencing user.age, where age is an int, then it would default to 0.
Got the same problem with java.lang.Boolean. Solved by using primitive boolean type instead.
<variable
name="var"
type="boolean" />