在哪里声明泛型
在引入泛型类型标记之前,您不能使用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
是无效的,因为它没有什么意义,并且不提供更多信息,而不仅仅是T
orNumber
或 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);
}
}