Что такое Java-эквивалент ковариантных и контравариантных модификаторов Kotlin?

Рассмотрим следующий интерфейс 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 (контравариантную) дисперсию?

Лучшая компания по разработке спортивных приложений
Лучшая компания по разработке спортивных приложений
Ищете лучшую компанию по разработке спортивных приложений? Этот список, несомненно, облегчит вашу работу!
Blibli Automation Journey - Как захватить сетевой трафик с помощью утилиты HAR в Selenium 4
Blibli Automation Journey - Как захватить сетевой трафик с помощью утилиты HAR в Selenium 4
Если вы являетесь веб-разработчиком или тестировщиком, вы можете быть знакомы с Selenium, популярным инструментом для автоматизации работы...
Фото ️🔁 Radek Jedynak 🔃 on ️🔁 Unsplash 🔃
Фото ️🔁 Radek Jedynak 🔃 on ️🔁 Unsplash 🔃
Что такое Java 8 Streams API? Java 8 Stream API
Деревья поиска (Алгоритм4 Заметки к учебнику)
Деревья поиска (Алгоритм4 Заметки к учебнику)
(1) Двоичные деревья поиска: среднее lgN, наихудшее N для вставки и поиска.
1
0
50
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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.)

Однако принципиальное отличие состоит в том, что вы можете передавать nulls в методы переменной 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 нет различий между сайтами объявлений.

Другие вопросы по теме