我有以下功能:
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都不够聪明,无法推断出这一点。您必须使用泛型方法签名作为"提示"。
in
和out
是Java不知道的概念,参见kotlin中的out关键字,了解kotlin中" in "关键字的一种用法
您的Java代码的适当替换将是:
fun <T : Person> bar(list: MutableList<T>) {
for(a in list) {
println(a.getName())
}
}
(感谢user31601在评论中指出这一点)