https://kotlinlang.org logo
#getting-started
Title
# getting-started
u

ursus

10/04/2021, 11:19 AM
lateinit 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?
j

Joffrey

10/04/2021, 11:24 AM
I don't know exactly how this is implemented behind the scenes, but I would say if you have to use
isInitialized
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.
☝️ 2
u

ursus

10/04/2021, 11:26 AM
I'm aware, I'm trying to retrofit something and I don't want
!!
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 places
j

Joffrey

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:
Copy code
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.
u

ursus

10/04/2021, 11:35 AM
yes .. but is it worth it? isn't laterinit var internally just that snippet?
j

Joffrey

10/04/2021, 11:41 AM
Well, we're not discussing
lateinit 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.
u

ursus

10/04/2021, 11:42 AM
I know what youre saying but im retrofitting some stupid android ui viewmodel handling which used laterinit var originally, but now turns out there is more cases where it might be null due to some initialization stuff
Copy code
override 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()
        }
    }
wondring if its too much
j

Joffrey

10/04/2021, 11:45 AM
I would 100% go for a nullable property instead here. All of these pieces of code could easily forget to check
isInitialized
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 property
u

ursus

10/04/2021, 11:50 AM
the issue its not "really" nullable .. if android was normal and get UI constructors, viewmodel would be semantically not null
1
its just we cannot have that, therefore one needs static stuff inside, which is racey, therefore null handling needs to be there, and just not call any lifecycle and wait for upstream to clear this screen -_-
s

Scott Kruse

10/04/2021, 3:05 PM
would just use DI framework for view model instantiation and not worry about any of this. To an Android lifecycle owner like activity, fragment, it’s viewmodel should always be available to it
e

ephemient

10/04/2021, 4:05 PM
::lateinitVar.isInitialized is a compiler intrinsic, doesn't use reflection, if that's what you're worried about
u

ursus

10/04/2021, 8:22 PM
yes thank you!
14 Views