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
不起作用?
在哪里声明泛型
在引入通用类型令牌T
之前,不能使用它。
在方法示例中,您试图在错误的位置声明T
,这是无效语法。你必须事先介绍一下。
然而,对于类示例,您已经将其放在了正确的位置。
在这里,您可以在类级别上介绍您的通用类型令牌:
public class Foo< HERE > { ... }
这就是你只针对一种方法的方法:
public < HERE > void foo(...) { ... }
有界泛型
在这两种情况下,您都可以绑定T
,如T 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
在上无效,因为它没有什么意义,并且不会提供比T
或Number
或简单的Object
更多的信息,这取决于您试图实现的目标。您可以在Java泛型方法中阅读更多关于这方面的内容:super不能使用吗?
通配符
通配符是另一回事。它们不是必须首先引入的泛型类型标记,例如T
。相反,它们明确了您想要接受的类型范围。
例如,像这样的方法
public static void foo(List<? super Dog> list) { ... }
可以用List<Dog>
、List<Animal>
或者甚至List<Object>
来调用。我们将这样的列表称为Dog
s的消费者。准确地说,这些列表是所有接受狗的列表,因此list.add(new Dog())
将起作用。
另一方面,我们有
public static void foo(List<? extends Dog> list) { ... }
其可以用CCD_ 23或CCD_。我们将这样的列表称为Dog
s的生产者(或提供者(。准确地说,这些列表是所有可以提供狗的列表。因此Dog dog = list.get(0)
将起作用。
您可以在what is PECS(Producer Extends Consumer Super(上阅读更多关于通配符的详细信息以及它们是如何工作的?
何时使用哪个
通常,当您仍然需要在整个代码中维护类型安全性时,您将使用泛型类型令牌T
。也就是说,当你需要时,给类型一个名称。否则使用通配符?
。
例如,假设您想要创建一个方法,该方法接受一个列表和一个要添加到其中的元素:
public static <T> void addToList(List<T> list, T element) {
list.add(element);
}
您需要引入T
,以确保列表的类型与给定的元素匹配。否则,有人可能会做addToList(dogs, cat)
,而这是你不想要的。
如果您不需要实际命名类型,也可以只使用通配符。例如,一种获取列表并打印其所有内容的方法:
public static void printAll(List<?> list) {
for (Object object : list) {
System.out.println(object);
}
}