Reputation: 10431
I know that Kotlin's Android Extensions creates synthetic properties + caching function to replace any need to call findViewById
:
All of these examples show that the similar java code would look like
private HashMap _$_findViewCache;
...
public View _$_findCachedViewById(int var1) {
if(this._$_findViewCache == null) {
this._$_findViewCache = new HashMap();
}
View var2 = (View)this._$_findViewCache.get(Integer.valueOf(var1));
if(var2 == null) {
var2 = this.findViewById(var1);
this._$_findViewCache.put(Integer.valueOf(var1), var2);
}
return var2;
}
public void _$_clearFindViewByIdCache() {
if(this._$_findViewCache != null) {
this._$_findViewCache.clear();
}
}
What I don't understand is how this prevents potential NPEs? var2 = this.findViewById(var1);
may still return null.
Using the example from that last link:
<TextView
android:id="@+id/welcomeMessage"
...
android:text="Hello World!"/>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
welcomeMessage.text = "Hello Kotlin!"
}
What type is welcomeMessage
? TextView
or TextView?
Upvotes: 1
Views: 1136
Reputation: 635
I got crash with NPE also in listener. I made ZoomRecyclerView, which call onZoom listener method onZoom when zoomed. This event is then propagated to activity, where i call synthetic imagebuttonview method, to set image resource to set zoom button image if item is zoomed or not. It simply crash on synthetic call.
Example :
zoomRecycler.onZoom {
// exception here : zoomButton must be not null
zoomButton.setImageDrawable(...)
}
zoomButton.click {
// calling zoom which raise onZoom inside zoomRecycler
zoomRecycler.toggleZoom();
}
Upvotes: 0
Reputation: 6862
While @CommonsWare's answer is correct, I also wanted to spare my 2 cents on this subject.
As @CommonsWare pointed out, you have to import relevant layout in order to be able to use Kotlin Extensions
. The tricky part here is, it's not only about importing relevant layout but also inflating the layout before you call it with Kotlin Extensions.
So, if you have something like below
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
welcomeMessage.text = "Hello Kotlin!"
setContentView(R.layout.activity_main)
}
}
You will still get
java.lang.IllegalStateException: welcomeMessage must not be null
And your app will crash.
Upvotes: 0
Reputation: 1007228
What I don't understand is how this prevents potential NPEs?
It doesn't. If you try referencing a widget that does not exist, you crash.
So long as your import
statements are only for the relevant layout for your Kotlin code, you should not wind up referencing a widget that does not exist. Where the problem comes in is if you accidentally import the synthetic properties from another layout.
For example, suppose you have a project with activity_main.xml
and scrap.xml
layouts, and your activity is:
package com.commonsware.android.myapplication
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import kotlinx.android.synthetic.main.scrap.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
scrapView.visibility = View.GONE
}
}
Here, we are referencing a scrapView
view in the scrap
layout. We have not inflated that layout, and so this crashes with an IllegalStateException
:
Caused by: java.lang.IllegalStateException: scrapView must not be null
at com.commonsware.android.myapplication.MainActivity.onCreate(MainActivity.kt:14)
What type is welcomeMessage? TextView or TextView?
Technically, it is TextView!
, where !
means "it's a platform type, so we don't know if it can be null
or not". Practically, TextView!
is used the same as TextView
, which is why you crash if it winds up being null
.
Upvotes: 3