Рассмотрим следующий интерфейс Kotlin:
interface Box<out T : SomeType> {
val item: T
}
Реализация этого с помощью Kotlin будет выглядеть примерно так:
data class BoxImpl<out T : SomeType>(override val item: T) : Box<T>
Теперь я хочу преобразовать этот интерфейс в Java:
public interface Box<T extends SomeType> {
T getItem();
}
Но реализация этого в Kotlin вызывает проблему:
data class BoxImpl<out T : SomeType>(private val item: T) : Box<T> {
override fun getItem(): T {
return item
}
}
Проблема в том, что компилятор Kotlin жалуется, что T
инвариантен для интерфейса Java, но ковариантен для реализации.
Итак, как указать out
(ковариантную) дисперсию в Java?
Кроме того, как бы вы указали in
(контравариантную) дисперсию?
Java не поддерживает вариантность сайта объявления, как это делает Kotlin. Он поддерживает только вариант использования сайта. Другими словами, вариантность может использоваться только для типов переменных и параметров, а не в определениях классов/интерфейсов.
Таким образом, в Java вы бы указали свой интерфейс и определения классов без отклонений, и каждый раз, когда вам нужно изменение, вы должны были бы объявлять переменную вместе с ним.
Ковариация использует ? extends
, а контравариантность использует ? super
, например:
Box<? extends Number> numberBox = new BoxImpl(1);
В Java нет вариации сайта объявления, как в Kotlin. То есть вы не можете указать дисперсию типа при его объявлении. Однако у него есть дисперсия по месту использования — вы можете указать дисперсию при использовании типа, например, как тип переменной.
Например,
// Kotlin
var foo: SomeGenericType<out T>
var bar: SomeGenericType<in T>
похож на:
// Java
SomeGenericType<? extends T> foo;
SomeGenericType<? super T> bar;
(Мнемоника для этого PECS.)
Однако принципиальное отличие состоит в том, что вы можете передавать null
s в методы переменной Java bar
,
bar.someMethod(null);
Но в Котлин нельзя ничего передать bar
— он буквально принимает Nothing
!
Если вы пытаетесь использовать универсальный тип из Java в своем коде Kotlin, я думаю, вам придется сделать это @UnsafeVariance
, чтобы подавить ошибку компилятора на данный момент.
// Of course, only do this if you are sure that Box is covariant!
data class BoxImpl<out T : SomeType>(private val item: T) : Box<@UnsafeVariance T>
Также ведутся дискуссии о добавлении возможности аннотировать параметры типа Java, чтобы компилятор Kotlin знал об их дисперсии. Смотрите это и это. Надеюсь, мы сможем получить это когда-нибудь в будущем. Хотя это не меняет того факта, что в Java нет различий между сайтами объявлений.