You can break immutabiliy of super class, even if you declare the immutable fields with val
in interface or abstract class.
interface Foo {
val bar: String // immutable
}
class FooImpl : Foo {
override var bar: String = "bar" // mutable
init {
bar = "changed"
}
}
println(FooImpl().bar) // changed
I expected above code will occurs compile error, but it works. Why is this possible in kotlin?
CodePudding user response:
val
doesn’t mean immutable. It means read-only. Even if you don’t override it as a var
you could make it a val
that returns a different value each time.
So expanding it to also be writable isn’t breaking any contract.
There is no way to enforce an open property to be immutable. Best you can do is use documentation that declares that it will not behave correctly if it doesn’t return the same value every time.
CodePudding user response:
From the language spec:
A property declaration D which overrides property declaration B should satisfy the following conditions.
- Mutability of D is not stronger than mutability of B (where read-only
val
is stronger than mutablevar
);- Type of D is a subtype of type of B ; except for the case when both D and B are mutable (
var
), then types of D and B must be equivalent.
So your overriding property can add mutability, and also use a more specific subtype (e.g. Int
instead of Number
) so long as the declaration isn't a var
(so you can't write a Double
to it via the interface).
One way you could look at it, is your original val
is sort of declaring a getBar(): String
function which you're overriding. By making it a var
, you're also defining a setString(string)
function in your own class.
If you try to access your object as Foo
then that setter doesn't exist, because it's not (implicitly) defined in the interface. It's a separate function on your class, just conveniently expressed by tweaking the property definition (in a defined, restricted way)