我正在尝试设计自己的编程语言,并且正在考虑泛型。我已经使用 Java 一段时间了,并且了解 extends
和 super
通用边界。
我正在阅读 this发布并尝试理解对下限的需求。
在我的语言中,如果您说 List<MyObject>
,我计划以与常规字段相同的方式处理泛型。 ,您可以存储 MyObject
, 或 MyObject
的任何子类型.有道理吗?
在帖子中,它们具有以下类层次结构:
class Person implements Comparable<Person> {
...
}
class Student extends Person {
...
}
然后他们有一个排序方法:
public static <T extends Comparable<T>> void sort(List<T> list) {
...
}
我认为,您应该能够发送 List<Student>
到这个方法。作为Student
延伸Person
, compare
方法将由它的父类(super class)处理,Person
.
The reason for the error message is that the compiler infers the type parameter of the sort method as T:=Student and that class Student is not
Comparable<Student>
. It isComparable<Person>
, but that does not meet the requirements imposed by the bound of the type parameter of method sort. It is required thatT
(i.e.Student
) isComparable<T>
(i.e.Comparable<Student>
), which in fact it is not.
以上对我来说没有任何意义......你应该能够做到student.compare(person)
,那么为什么这行不通呢?
也许是说 Student
应该实现它自己的可比较方法,以便 Student
在比较中有发言权吗?您不需要做任何特别的事情,只需覆盖 Person
的方法。您将无法保证您正在与另一个 Student
进行比较, 但可以用 instanceof
检查.
我在这里遗漏了什么吗?
经过所有这些思考,我现在想知道 extends
的目的是什么是。据我了解,在 List<MyType>
中,你只能放一个MyType
在,不是它的任何子类。如上所述,这对我来说没有任何意义,您应该能够像字段一样将任何子类放入列表中。
我应该说清楚,不是“为什么它在 Java 中不起作用”,而是“为什么它在泛型理论中不起作用”。我只是标记了 java,因为那是我进行比较的地方。
最佳答案
首先:方法声明
public static <T extends Comparable<T>> void sort(List<T> list)
对我来说意义不大。我觉得应该是
public static <T extends Comparable<? super T>> void sort(List<T> list)
那么就可以写成sort(listOfStudents)
.现在我将解释上界和下界通配符的优点:
类型参数的多态性不会转移到它的泛型类型
这意味着学生列表 ( List<Student>
) 不是人员列表 ( List<Person>
)。像这样的指令
List<Person> list = new List<Student>();
在 Java 中会失败。原因很简单:list.add(new Person());
对于学生名单是非法的,但对于人员名单则不是。
上限通配符
但是也许你有一个函数不关心对象是否是子类。例如:你可以有这样的方法:
void printAll(List<Person> list)
他们只是将一些关于所有人的数据打印到标准输出。如果你有一份学生名单 ( List<Student> listOfStudents
) 你可以这样写:
List<Person> listOfPersons = new ArrayList<>();
for (final Student student : listOfStudents) {
listOfPersons.add(student);
}
printAll(listOfPersons);
但您可能会发现这不是一个很好的解决方案。另一种解决方案是对 printAll
使用上限通配符 :
void printAll(List<? extends Person> list)
你可以这样写 Person person = list.get(0)
在printAll
.但是你不能写 print.add(new Person())
因为list
可以是学生列表或其他内容。
下界通配符
现在在另一个方向上也是如此:假设您有一个生成一些学生并将他们放入列表中的函数。像这样:
void generateStudents(List<Student> list) {
for (int i = 0; i < 10; ++i) {
list.add(new Student());
}
}
现在您有一个人员列表 ( List<Person> listOfPersons
) 并希望在此列表中生成学生。你可以写
List<Student> listOfStudents = new ArrayList<>();
generateStudents(listOfStudents);
for (Student student : listOfStudents) {
listOfPersons.add(student);
}
您可能会再次看到,这不是一个很好的解决方案。您还可以更改 generateStudents
的声明到
void generateStudents(List<? super Student> list)
现在,你可以写generateStudents(listOfPersons);
.
关于java - 为什么 Java 在泛型方面有下限?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35714431/