Problem:
Kotlin by lazy throws unexpected NullPointerException when reached. Through debugger, when reached, this is properly evaluated and findViewById<ShapeableImageView>(R.id.image_view_icon) returns valid ImageView view, so I can't figure out why this happens - why is NullPointerException thrown if this is not null and if view exists and is already inflated at the point of lazy initialization?
Use case example:
I have custom Android view component - simple label view SimpleLabelView. This components supports setting icon and it's implementation defaults to setting it as a compound drawable to label TextView..
Now, I want to create a new component CompactIconLabelView which extends existing SimpleLabelView. New CompactIconLabelView uses different layout, and now I want to change the behavior of setting the icon - instead of setting it as a drawable to label, I want to set that drawable to custom ImageView that is contained in that new drawable. In order to do that, I need to initialize given ImageView lazily
Error
Caused by: java.lang.NullPointerException: Attempt to invoke interface method 'java.lang.Object kotlin.Lazy.getValue()' on a null object reference
at hr.simplifystay.sharedmodelslibrary.customViews.label.CompactIconLabelView.getIconImageViewThatCrashes(CompactIconLabelView.kt:40)
at hr.simplifystay.sharedmodelslibrary.customViews.label.CompactIconLabelView.setIcon(CompactIconLabelView.kt:37)
at hr.simplifystay.sharedmodelslibrary.customViews.label.SimpleLabelView.<init>(SimpleLabelView.kt:157)
at hr.simplifystay.sharedmodelslibrary.customViews.label.CompactIconLabelView.<init>(CompactIconLabelView.kt:24)
at hr.simplifystay.sharedmodelslibrary.customViews.label.CompactIconLabelView.<init>(CompactIconLabelView.kt:18)
at hr.simplifystay.sharedmodelslibrary.customViews.label.CompactIconLabelView.<init>(Unknown Source:14)
Code
SimpleLabelView
open class SimpleLabelView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet?,
defStyleAttr: Int = R.attr.labelViewStyle,
defStyle: Int = R.style.Theme_Simplify,
@LayoutRes layoutResId: Int = R.layout.layout_simple_label
) : ConstraintLayout(context, attrs, defStyleAttr, defStyle) {
@StyleableRes
private val iconAttr = R.styleable.SimpleLabelView_icon
open var icon: Drawable? = null
set(value) {
field = value
label.setCompoundDrawablesRelativeWithIntrinsicBounds(value, null, null, null)
}
val label: MaterialTextView
init {
val view = inflate(context, layoutResId, this)
... some initializing code
icon = typedArray.getDrawable(iconAttr) // Line 157 from stacktrace
... more code
}
}
CompactIconLabelView
open class CompactIconLabelView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet?,
defStyleAttr: Int = R.attr.labelViewStyle,
defStyle: Int = R.style.Theme_Simplify,
@LayoutRes layoutResId: Int = R.layout.layout_compact_icon_label
) : SimpleLabelView(context, attrs, defStyleAttr, defStyle, layoutResId) {
override var icon: Drawable? = null
get() = super.icon
set(value) {
field = value
// This works
if (!(::iconImageView.isInitialized))
iconImageView = findViewById(R.id.image_view_icon)
iconImageView.setImageDrawable(value)
// This crashes
iconImageViewThatCrashes.setImageDrawable(value) // Line 37 from stacktrace
}
private val iconImageViewThatCrashes: ShapeableImageView by lazy { findViewById(R.id.image_view_icon) } // Line 40 from stacktrace
private lateinit var iconImageView: ShapeableImageView
}
So I am wondering, am I doing anything wrong or is this some kind of a bug in Kotlin?