在从列表中检索项目时需要强制转换吗?



我有以下功能:

fun bar(list: MutableList<in Person>) {
for(a in list) {
println(a.getName())
}
}

问题是a被认为是Any类型,我需要先转换为Person。这是应该的吗,还是我做错了什么?
在Java中,如果我用extends Person声明,我将不需要强制转换。
。如果我有:

public <T extends Person> void bar(List<T> list)

您给出的Java方法签名是针对泛型方法的(即绑定类型变量的方法)。这允许您指定列表必须具有该元素类型,从而允许您安全地检索和插入元素到列表中,并且知道它们属于该类型。你也可以在Kotlin中这样做:

public <T extends Person> void bar(List<T> list);

等价于

fun <T: Person> bar(list: MutableList<T>)
但是,您在问题中给出的Kotlin方法签名是而不是泛型,但有一个通配符泛型参数。在Java中也可以这样做:
fun bar(list: MutableList<in Person>)

等价于

public void bar(List<? super Person> list);

这与上面的泛型方法签名不同。在泛型方法中,我们可以通过从列表中取出元素来创建类型为T的变量,并且我们知道这些是Person的子类型。由于list被绑定为元素类型T,我们也知道可以将这样的变量插入到列表中。

对于非泛型的通配符签名,我们不能这样做。在Java和Kotlin两种情况下,我们都有一个方法可以接受元素类型为Person的超类型的任意列表。因此,当我们从列表中取出元素时,我们所确定的是它们是Person的超类型,而符合此条件的唯一类型是Object/Any。然而,通配符上的约束确实告诉我们,在列表中插入Person类型(或子类型)的元素应该是可以的。

为了完整起见,反过来也是可能的:

public void bar(List<? extends Person> list);

等价于

fun bar(list: MutableList<out Person>)

在这里,该方法可以接受元素类型为Person的子类型的任何列表。我们知道,如果从列表中提取一个元素,它将符合Person接口,因此可以将其赋值给Person类型的变量。但是,我们不知道可以将什么类型插入到列表中,因为我们不知道列表接受的Person的确切子类型。尽管我们知道刚刚从列表中提取的值必须是可接受的插入类型,但我相信Kotlin和Java都不够聪明,无法推断出这一点。您必须使用泛型方法签名作为"提示"。

inout是Java不知道的概念,参见kotlin中的out关键字,了解kotlin中" in "关键字的一种用法

您的Java代码的适当替换将是:

fun <T : Person> bar(list: MutableList<T>) {
for(a in list) {
println(a.getName())
}
}

(感谢user31601在评论中指出这一点)

最新更新