Answers:
好吧,前两个没有什么区别-它们只是为type参数(E
或T
)使用了不同的名称。
第三个不是有效的声明- ?
用作提供类型实参时使用的通配符,例如,意味着引用某种类型的列表,但我们不知道是什么。List<?> foo = ...
foo
所有这些都是泛型,这是一个很大的话题。您可能希望通过以下资源了解它,尽管当然还有更多可用的方法:
T
和E
-他们只是标识符。你可以写KeyValuePair<K, V>
例如。?
虽然有特殊的意义。
前面的答案解释了类型参数(T,E等),但是不解释通配符“?”或它们之间的差异,因此我将解决。
首先,要清楚一点:通配符和类型参数不相同。在类型参数定义代表范围类型的变量(例如T)的情况下,通配符则没有:通配符仅定义了一组可用于泛型类型的允许类型。没有任何限制(extends
或super
),通配符的意思是“在这里使用任何类型”。
通配符始终位于尖括号之间,并且仅在通用类型的上下文中才有意义:
public void foo(List<?> listOfAnyType) {...} // pass a List of any type
决不
public <?> ? bar(? someType) {...} // error. Must use type params here
要么
public class MyGeneric ? { // error
public ? getFoo() { ... } // error
...
}
它们重叠的地方变得更加混乱。例如:
List<T> fooList; // A list which will be of type T, when T is chosen.
// Requires T was defined above in this scope
List<?> barList; // A list of some type, decided elsewhere. You can do
// this anywhere, no T required.
方法定义可能有很多重叠之处。在功能上,以下内容相同:
public <T> void foo(List<T> listOfT) {...}
public void bar(List<?> listOfSomething) {...}
因此,如果存在重叠,为什么要使用另一个?有时候,说实话,这只是样式:有人说,如果您不需要类型参数,则应该使用通配符只是为了使代码更简单/更易读。我在上面解释的一个主要区别是:类型参数定义类型变量(例如T),您可以在范围的其他地方使用它。通配符没有。否则,类型参数和通配符之间有两个大区别:
类型参数可以具有多个边界类。通配符不能:
public class Foo <T extends Comparable<T> & Cloneable> {...}
通配符可以具有下限;类型参数不能:
public void bar(List<? super Integer> list) {...}
在上面,List<? super Integer>
定义Integer
为通配符的下限,这意味着List类型必须是Integer或Integer的超类型。泛型类型限制超出了我想要详细介绍的范围。简而言之,它允许您定义泛型类型可以是哪些类型。这使得可以多态地处理泛型。例如:
public void foo(List<? extends Number> numbers) {...}
你可以通过一个List<Integer>
,List<Float>
,List<Byte>
,等了numbers
。没有类型限制,这将不起作用-泛型就是这样。
最后,这是一个方法定义,该定义使用通配符来执行某些我认为您无法以其他方式执行的操作:
public static <T extends Number> void adder(T elem, List<? super Number> numberSuper) {
numberSuper.add(elem);
}
numberSuper
可以是Number列表或Number的任何超类型(例如List<Object>
),并且elem
必须是Number或任何子类型。有了所有的边界,编译器就可以确定the .add()
是类型安全的。
最常用的类型参数名称是:
E - Element (used extensively by the Java Collections Framework)
K - Key
N - Number
T - Type
V - Value
S,U,V etc. - 2nd, 3rd, 4th types
您将看到在Java SE API中使用的这些名称