java - 为什么 Java 在泛型方面有下限?

标签 java language-design

我正在尝试设计自己的编程语言,并且正在考虑泛型。我已经使用 Java 一段时间了,并且了解 extendssuper通用边界。

我正在阅读 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 is Comparable<Person> , but that does not meet the requirements imposed by the bound of the type parameter of method sort. It is required that T (i.e. Student ) is Comparable<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/

相关文章:

java - 如何将值列表传递到 Crystal 报表中

墙内的Java迷宫并获得所有可能的路径

java - 哪个 jar 包含 IsoOutputStream 和 IsoBufferWrapperImpl 类

garbage-collection - 如何在没有 gc 的情况下实现闭包?

java - .NET 到 Java 教程

java - 如何在 Java 中将已排序数组重置为未排序数组?

python - 从函数返回多个值的优雅方法

parsing - 用户定义的中缀运算符的解析器

exception-handling - 语言设计(异常(exception)): Why `try` ?