什么是Java中与Kotlin的协变和逆变修饰符等价的东西?

lp0sw83n  于 2023-02-20  发布在  Java
关注(0)|答案(2)|浏览(129)

考虑以下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接口上是不变的,但是在实现上是协变的
那么,如何在Java中指定out(协变)方差呢?
另外,如何指定in(逆变)方差?

pxq42qpu

pxq42qpu1#

Java不像Kotlin那样有声明位置的变量,也就是说,你不能在你 * 声明 * 一个类型的时候指定它的变量,但是,它有使用位置的变量--你 * 可以 * 在你 * 使用 * 一个类型的时候指定变量,例如,作为一个变量的类型。
例如,

// Kotlin
var foo: SomeGenericType<out T>
var bar: SomeGenericType<in T>

类似于:

// Java
SomeGenericType<? extends T> foo;
SomeGenericType<? super T> bar;

(The其助记符为PECS。)
但是,一个关键的区别是,您可以将null传递到Java bar变量的方法中,

bar.someMethod(null);

但是你不能把任何东西传递到Kotlin bar中--它实际上需要Nothing!
如果您试图在Kotlin代码中使用Java中的泛型类型,我认为您现在必须将其设置为@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编译器知道它们的变化。参见thisthis。希望我们将来能得到它。尽管这不会改变Java中没有声明站点变化的事实。

abithluo

abithluo2#

Java不像Kotlin那样支持声明位置的变量,它只支持使用位置的变量,换句话说,变量只能用在变量和参数类型上,不能用在类/接口定义中。
所以在Java中,你可以指定你的接口和类定义,而不需要变量,每次你需要变量的时候,你都可以用它来声明变量。
协方差使用? extends,反方差使用? super,例如:

Box<? extends Number> numberBox = new BoxImpl(1);

相关问题