I have subclasses which have `override val`s, whic...
# getting-started
y
I have subclasses which have `override val`s, which are initialized in the subclass's
init
block. I am now getting a warning that such properties must be "initialized, finalized or abstract" and that this behavior is deprecated (soon to be a hard error) 1. is this a new Kotlin change? 2. what is the idiomatic way to solve this, since some of these initialization are a little complex? a dedicated
private fun
initializer for each initialized value?
y
I think you can place the
init
block before the `override val`s
y
(unless I'm missing something) you can't:
Copy code
class Bar : Baz() {
    override val foo: Foo // warning: property must be initialized, etc.
    init { 
        foo = something() 
    }
}
however
Copy code
class Bar : Baz() {
    init { 
        foo = something() // error: variable cannot be initialized before declaration
    }
    override val foo: Foo
}
sigh. dedicated initializers are making the code quite cumbersome
c
Is this a new warning for you? Is it from ide or Kotlin compiler?
y
from the IDE, and it appears to be a new warning (a coworker does not have it)
I'm going to guess that this was introduced in Kotlin 1.9.0
c
Did you recently upgrade to Kotlin 1.9? Intellij 2023.2 was released last week, could that be it?
y
yes to both. pretty sure I recently got a popup about said update.
oh well. not a huge change anyway
c
If it’s a Kotlin change new to 1.9 it should be in the release notes.
x
Both of the snippets that you have above are expected to provide errors. In the first case, you’re trying to reassign a
val
and in the second, the variable is not yet declared. For the first case, I think you may have a confusion with the pipeline of a class initialisation in Kotlin. The constructor arguments are those arguments needed to be called to create an instance of the class. The properties outside the constructor and the
init
block are called afterwards, once you have an instance of the class. For instance, the following code:
Copy code
class Bar(val constructorArgument: String) {
    val afterConstructedProperty: String

    init {
        afterConstructedProperty = "stringValue"
    }
}
Has this equivalent code in Java:
❤️ 1
You can see that the
constructorArgument
is an argument of the Java constructor
public Bar(@NotNull String _constructorArgument_)
, while
constructedProperty
is declared and assigned after the instance is initialised using the
super()
call
y
I see. but in the first case, why was this previously allowed?
x
It feels strange for me, as I understand the execution order as follows: 1. Constructor 2. Block of properties outside the constructor 3.
init
block I wonder now if there are cases where this is different. Anyway, it feels confusing to have the initialisation of a constructor argument in the
init
block of the class
c
not seeing that (or any) warning for this code (IntelliJ 2023.2, Kotlin 1.9):
Copy code
public abstract class Baz {
    public abstract val x : Int
}

public class Bar : Baz() {
    override val x: Int

    init {
        x = 5
    }
}

public class Bar1 : Baz() {
    override val x: Int

    init {
        x = something()
    }

    private fun something() : Int = 5
}
y
@Chris Lee can you try this?
Copy code
sealed class Baz {
    abstract val name: String

    class Bar : Baz() {
        override val name
        
        init {
            name = "foobar!"
        }
    }
}
c
In my setup (IntelliJ 2023.2, Kotlin 1.9) that won’t compile:
This property must either have a type annotation, be initialized or be delegated
For similar patterns I tend to only use constructor parameters; in the case where you want to insulate construction from the caller use a companion object:
Copy code
public sealed class Baz3(public open val name : String) {

    public class Bar3 private constructor(public override val name: String) : Baz3(name) {
        public companion object {
            public fun create(): Bar3 {
                return Bar3("abc")
            }
        }
    }
}
❤️ 1
y
@Chris Lee thank you! I have changed the Kotlin compiler version (which was, IIRC, 1.8.23) to 1.9 and now it does not compile. (annoyingly I can't change it back) the pattern with the companion object would also work here, `constructor`s are not very nice with complex initializations, and can't handle fallible initializations
(well they can if you throw)
c
good stuff. I rarely use `init`blocks and, when I do, they do very little (a few lines of simple code). Any more complex setup is outside of the class definition, in companion objects or factory classes.
❤️ 1
k
This compiles ok:
Copy code
sealed class Baz {
    abstract val name: String

    class Bar : Baz() {
        override val name: String

        init {
            name = "foobar!"
        }
    }
}
y
are you on 1.9?
k
Yes
And IJ 2023.2
Note I've added
: String
to the override val declaration
😔 1
y
oh yeah. duh.
are you getting any IDE warnings?
k
Just a minor warning that the val declaration and init block can be merged into one statement, but that's to be expected. If the init block were more complicated, the warning would go away.