-
Notifications
You must be signed in to change notification settings - Fork 38
Description
In the draft proposal, there's a part that allows for overrides to have value parameters with contravariant nullability:
The method’s parameter types may have wider nullability than the corresponding superparameters' types, meaning, it suffices for superparameters to be type-convertible to the overriding method’s parameter types (while base types have to follow the normal rules of the compiler so overriding works as expected).
That might be a problem for the Kotlin compiler since in pure Kotlin value parameters are not contravariant in overrides and we apply the same logic when looking at the annotated Java code.
The reason we don't allow contravariant parameters in override is basically the same as in Java: they might be considered as overloads. In Java, one can not override with a wider-typed parameter (types should be equal). The same applies for Kotlin, but we have nullability as a built-in part of the type system.
interface A {
fun foo(x: String)
}
class B : A {
override fun foo(x: String) {
}
@JvmName("fooNullable")
fun foo(x: String?) {} // is a different overload for `foo`
}
fun main() {
B().foo("") // resolved to the not-nullable
B().foo(null) // resolved to nullable
}
That means that when we see contravariant nullability in Java annotations, such code is assumed to be illegal thus we ignore annotations info for such parts completely as inconsistent:
interface A {
void foo(@NotNull String x);
}
class B implements A {
@Override
public void foo(@Nullable String x) {
}
}
B::foo
has signature like fun foo(x: String!)
, i.e. it has unknown nullability from Kotlin point-of-view.
But at the same time it's fine transforming from unknown nullness to both NotNull
and Nullable
:
interface A {
void foo(String x, String y);
}
class B implements A {
@Override
public void foo(@Nullable String x, @NotNull String y) {
}
}
In the above example B::foo
is perceived by Kotlin as fun foo(x: String?, y: String)