函数结果如 List<?在 Kotlin 中扩展 Base>

Function result like List<? extends Base> in Kotlin

我在 Java 中有这个,但不知何故我无法找到在 Kotlin 中指定 getStuff() 的 return 类型的正确方法。

class Base {
}

class Extended extends Base {
}

List<? extends Base> getStuff() {
    return new ArrayList<Extended>();
}

List<Extended> foo = new ArrayList<>();
foo.addAll((List<Extended>)getStuff());

当我像这样将 getStuff() 函数转换为 Kotlin 时

fun getStuff(): List<Base> { ... }

我在 Java 的 foo.addAll((List<Extended>)getStuff()); 中得到一个编译器错误说

List 无法转换为 List

我发现的唯一方法是使用 getStuff() return List<*> 但这似乎不对。

这似乎是 Kotlin 支持 declaration-site 方差但 Java 不支持的方式造成的怪癖。在 Kotlin 中 <? extends Base> 等价于 <out Base>。在 Kotlin 中,read-only List 接口在声明站点已经是 <out T>,因此在使用 read-only 列表时通常不需要指定 out。但是当一个 List 返回到 Java 代码时,Java 看不到声明位置的差异,因此它认为它是一个不变的 T。你可以在 use-site 处显式标记差异:

fun getStuff(): List<out Base> { ... }

但是 Java 似乎仍然认为它在我的实验中是不变的。我认为 Kotlin 不会费心应用 use-site 方差,因为它在 Kotlin 方面是多余的。

在 Java 方面解决这个问题的一种方法是转换为协变类型,然后再转换为您的其他类型:

List<Extended> foo = new ArrayList<>();
foo.addAll((List<Extended>)(List<? extends Base>)getStuff());

或者,你可以在 Kotlin 中将其更改为 MutableList,这样使用 use-site 方差并不多余:

fun getStuff(): MutableList<out Base> { ... }

无论哪种方式,这都是不安全的转换,因为 Java 中的 getStuff() 返回的 List 或 Kotlin 可能包含 Base 的子类型但不是 Extended 的元素。这可以解释为什么 Kotlin 设计者没有尝试“修复”您 运行 遇到的问题。它只会启用不安全的代码。