-1
class Employee<T extends Number> {  // valid
}

class Employee<? extends Number> { // invalid
}

private static void test(List<? super Number> list1) { // valid
}

private static <T>void test(List<T super Number> list1) { // invalid
}

究竟有什么区别?以及T何时使用什么?

为什么使用类定义?不起作用,但它可以使用List,为什么T使用类定义但不能使用List

4

1 回答 1

2

在哪里声明泛型

在引入泛型类型标记之前,您不能使用T它。

在您的方法示例中,您尝试T在错误的位置声明,这是无效的语法。你必须事先介绍它。

但是,对于类示例,您已将其放在正确的位置。

在这里,您可以在类范围内介绍您的泛型类型标记:

public class Foo< HERE > { ... }

这就是你如何只为一种方法做的:

public < HERE > void foo(...) { ... }

有界泛型

在这两种情况下,您都可以绑定您的T, likeT extends Number然后相应地使用它:

public class Foo<T extends Number> { ... }

// or

public <T extends Number> void foo(...) { ... }

在你介绍了你的之后T,你会像那样使用它。所以List<T>,作为一个例子。

public <T extends Number> void foo(List<T> list) { ... }

请注意,它T super Number是无效的,因为它没有什么意义,并且不提供更多信息,而不仅仅是TorNumber或 simple Object,具体取决于您要实现的目标。您可以在Java 泛型方法中阅读更多相关信息:不能使用 super 吗?


通配符

通配符是另一回事。它们不是您必须首先引入的通用类型标记,例如T. 相反,它们阐明了您想要接受的类型范围。

例如像这样的方法

public static void foo(List<? super Dog> list) { ... }

可以用 a List<Dog>、 aList<Animal>甚至 a调用List<Object>。我们称这样的列表为s 的消费者Dog。准确地说,这些都是可以接受 dog 的列表,所以list.add(new Dog())会起作用。

另一方面,我们有

public static void foo(List<? extends Dog> list) { ... }

可以用 aList<Dog>或 a来调用List<Chihuahua>。我们称这样的列表为 s 的生产者(或提供者)Dog。准确的说,这些都是可以提供狗的名单。所以Dog dog = list.get(0)会起作用。

您可以在什么是 PECS(生产者扩展消费者超级)中阅读更多关于什么是通配符以及它们如何工作的详细信息?


什么时候用哪个?

T通常,当您实际上仍需要在整个代码中维护类型安全时,您会使用泛型类型标记。即,当您需要能够为类型命名时。否则,您使用通配符?

例如,假设您要创建一个方法,该方法接受一个列表和一个要添加到其中的元素:

public static <T> void addToList(List<T> list, T element) {
    list.add(element);
}

您需要引入T以确保列表的类型与给定元素匹配。否则有人可以做addToList(dogs, cat),这是你不想要的。

如果您不需要实际命名 type,您也可以只使用通配符。例如,一个采用列表并打印其所有内容的方法:

public static void printAll(List<?> list) {
    for (Object object : list) {
        System.out.println(object);
    }
}
于 2021-05-30T13:50:27.723 回答