Hello, World! I've faced this little issue a few t...
# announcements
b
Hello, World! I've faced this little issue a few times, and not sure what the "elegant" solution is. I have a non nullable
val
which is initialized in
init
, but the super's
init
calls a method that accesses it (so: before it's initialized). In that method I can do a
if (myVar != null) { ...
which works. But now I get a
Unnecessary safe call on a non-null receiver
warning πŸ™‚ The compiler tells me this null check is useless since this val is non nullable... But here it can be
null
. I can of course ignore / suppress the warning but I was wondering if there's a deeper truth to this πŸ™‚ Ideas?
a
a code sample would help to understand which property is accessed where
b
all right, be right back πŸ˜›
here:
Copy code
open class Parent {
    init {
        doStuff()
    }

    open fun doStuff() {}
}

class Child : Parent() {
    private val s: String

    init {
        s = "Hello, World!"
    }

    override fun doStuff() {
        println(s?.length) // <- warning here because `?.` on something supposedly non nullable (but null)
    }
}
(to be fair: there's also a warning on Parent's
init
calling non final
doStuff
. But in my case Parent doesn't belong to me (it's actually an Android framework's class)
l
A solution is to mark is as nullable, as it is based on your use case.
a
but in doStuff, how can s be null?
l
@arjun Because the
init
block of
Parent
is called before the one of
Child
.
m
so if you're intended strategy is to have a non-null val s, why would you even want to have it be accessed in a overrideable fun that accesses it before it is set to begin with?
otherwise, just do
private val s: String?
right?
b
I guess the fact that this method was actually called was a surprise πŸ˜‰ and the
?.
was a "workaround"
in my case marking it as nullable is a bit unfortunate because if you look at the code, it being initialized in
init
gives the impression that in can never be
null
indeed
m
there are still some questions. is s always going to be the same value? why not initialize directly instead of within init? you could make s a static const like so:
Copy code
class Child : Parent() {
    override fun doStuff() {
        println(s.length)
    }
    companion object {
        private const val s: String = "Hello World"
    }
}
b
no it's not static, it's really a field of this class
it's true I could initialize it directly (but doesn't change much)
m
I'm trying to understand. But if your class has no constructor args, and always initializes a certain field the same way for each of it's instances (which I'm assuming with your example code), why not use a static? Or is the init random?
b
not sure if you know Android but in my case it's actually a sub view (initialized with a
findViewById
.) So a
val
but definitely not random nor static πŸ™‚
m
how about...
Copy code
class Child : Parent() {
    val s: String
        get() = "Hello World"
    
    override fun doStuff() {
        println(s.length)
    }
}
otherwise I'm out of ideas πŸ˜›
l
Another possibility that doesn't suit all use cases: using
lazy(NONE) { }
b
I think my example was an illustration of the general issue, but the actual details of my case matter too much πŸ™‚ I now think there's no general solution.
thanks for sharing thoughts/ideas anyway people!
πŸ‘ 1
(at the least I learned that the warning about calling a non final method in
init
is very useful and shouldn't be ignored! - even though in my case it's "not my fault" as this happens in the framework πŸ˜›)