ursus
10/04/2021, 11:19 AMlateinit var .... ::viewModel.isInitialized
what is this at runtime? should I be vary of calling it too frequently? or is it just null check and assert?Joffrey
10/04/2021, 11:24 AMisInitialized
often, lateinit
is probably not the right approach for your property. You should consider using a nullable type without lateinit
instead.
lateinit
is interesting when you know better than the compiler that your variable is going to be initialized in time. It allows you to use a non-nullable type even though theoretically there is a moment when the variable is actually null (before initialization). If you have to check in multiple places whether the variable is initialized, it means you don't really control this initialization that well and you should instead declare the value as nullable so the compiler can properly help you deal with the nullability.ursus
10/04/2021, 11:26 AM!!
everywhere, so wondering if I should have nullable backing field and then a public non null asserted getter, and use the nullabe internally .. or just lateinit var as it is, and use isInitialized in the few placesJoffrey
10/04/2021, 11:33 AM!!
all over the place would definitely not be the way to go in any case. As for your dilemma, I guess it may also depend on whether you want to expose the var
part. Can this property be changed from outside?
If you can encapsulate the nullability and expose a public non-null val
property instead, I would personally go that route. It's also an opportunity to write nicer error messages in case the thing that is supposed to initialize the property didn't happen:
val publicProp: YourType
get() = privateNullableProp ?: error("this well-known event that should have initialized this property did not happen")
Using a lateinit var
that was not initialized would break in a less informative way.ursus
10/04/2021, 11:35 AMJoffrey
10/04/2021, 11:41 AMlateinit var
accesses per se, we're discussing if (isInitialized)
in the internal part of the code.
What I meant is that if you intend to use if (isInitialized)
it would be better to instead have a clear separation between the internal nullable property that requires null checks (and is initialized by internal events) and the public non null val
property that doesn't require any particular checks and have nice error messages in case of internal errors.ursus
10/04/2021, 11:42 AMoverride fun onCreateView(
inflater: LayoutInflater, container: ViewGroup, savedViewState: Bundle?
): View {
val view = super.onCreateView(inflater, container, savedViewState)
val activity = activity!!
if (!::viewModel.isInitialized) {
val parentComponent = Components.getOrNull(parentControllerType())
if (parentComponent != null) {
val savedState = savedViewState?.getParcelable<SS>(KEY_SAVED_STATE)
viewModel = onCreateViewModel(parentComponent, savedState)
viewModel.init()
} else {
LOG.w("ParentComponent '${parentControllerType()}' not found in Controller. Should reset backstack.")
(activity as ControllerHost).onControllerParentComponentNotFound(this)
}
}
if (::viewModel.isInitialized) {
onViewCreated(activity, viewBinding!!, viewModel, savedViewState)
}
return view
}
override fun onAttach(view: View) {
super.onAttach(view)
val activity = activity!!
if (::viewModel.isInitialized) {
onViewAttached(activity, viewBinding!!, viewModel, scope)
}
}
override fun onSaveViewState(view: View, outState: Bundle) {
super.onSaveViewState(view, outState)
if (::viewModel.isInitialized) {
val savedState = viewModel.toSavedState()
if (savedState != null) {
outState.putParcelable(KEY_SAVED_STATE, savedState)
}
}
}
override fun onDestroy() {
super.onDestroy()
if (::viewModel.isInitialized) {
viewModel.clear()
}
}
Joffrey
10/04/2021, 11:45 AMisInitialized
and that's exactly what Kotlin nullability checks in the compiler are about. Using lateinit
here and using isInitialized
like this is like giving up null safety for this propertyursus
10/04/2021, 11:50 AMScott Kruse
10/04/2021, 3:05 PMephemient
10/04/2021, 4:05 PMursus
10/04/2021, 8:22 PM