https://kotlinlang.org logo
#announcements
Title
# announcements
b

bod

06/30/2020, 11:33 AM
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

arjun

06/30/2020, 11:43 AM
a code sample would help to understand which property is accessed where
b

bod

06/30/2020, 11:44 AM
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

louiscad

06/30/2020, 11:56 AM
A solution is to mark is as nullable, as it is based on your use case.
a

arjun

06/30/2020, 11:58 AM
but in doStuff, how can s be null?
l

louiscad

06/30/2020, 11:59 AM
@arjun Because the
init
block of
Parent
is called before the one of
Child
.
m

Michael de Kaste

06/30/2020, 12:03 PM
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

bod

06/30/2020, 12:04 PM
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

Michael de Kaste

06/30/2020, 12:10 PM
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

bod

06/30/2020, 12:32 PM
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

Michael de Kaste

06/30/2020, 12:40 PM
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

bod

06/30/2020, 12:41 PM
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

Michael de Kaste

06/30/2020, 12:47 PM
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

louiscad

06/30/2020, 12:51 PM
Another possibility that doesn't suit all use cases: using
lazy(NONE) { }
b

bod

06/30/2020, 12:53 PM
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 😛)