Javagenerics的“?”,“E”和“T”有什么区别?
我遇到这样的Java代码:
public interface Foo<E> {} public interface Bar<T> {} public interface Zar<?> {}
上述三者之间的区别是什么?他们在Java中将这种types或接口声明称为什么?
那么前两者之间没有区别 – 他们只是使用不同的名称作为types参数 ( E
或T
)。
第三个不是有效的声明 – ?
用作提供types参数时使用的通配符 ,例如List<?> foo = ...
表示foo
引用某种types的列表,但我们不知道是什么。
所有这些都是generics ,这是一个相当大的话题。 您可能希望通过以下资源来了解它,当然还有更多可用的资源:
- generics的Java教程
- generics的语言指南
- Java编程语言中的generics
- Angelika Langer的Javagenerics常见问题解答 (大规模和全面;更多供参考)
这比其他任何事情都更公平。
-
T
是为了一个types -
E
意味着是一个元素(List<E>
:元素列表) -
K
是Key(在Map<K,V>
) -
V
是值(作为返回值或映射值)
它们完全可以互换(即使在同一声明中也有冲突)。
以前的答案解释了types参数(T,E等),但不解释通配符“?”,或者它们之间的差异,所以我会解决这个问题。
首先,要明确:通配符和types参数是不一样的。 在types参数定义了一个表示范围types的variables(例如T)的情况下,通配符不会:通配符只是定义了一组可用于generics的允许types。 没有任何限制( extends
或super
),通配符意味着“在这里使用任何types”。
通配符总是在尖括号之间,它只在genericstypes的上下文中有意义:
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.
方法定义可能有很多重叠。 以下是function上相同的:
public <T> void foo(List<T> listOfT) {...} public void bar(List<?> listOfSomething) {...}
所以,如果有重叠,为什么要使用其中一个? 有时老实说就是风格:有些人说如果你不需要types参数,你应该使用通配符来使代码更简单/更具可读性。 我在上面解释了一个主要区别:types参数定义了一个typesvariables(例如,T),您可以在范围中的其他地方使用它; 通配符不。 否则,types参数和通配符有两个很大的区别:
types参数可以有多个边界类; 通配符不能:
public class Foo <T extends Comparable<T> & Cloneable> {...}
通配符可以有下限; typesparams不能:
public void bar(List<? super Integer> list) {...}
在上面的List<? super Integer>
List<? super Integer>
将Integer
定义为通配符的下限,意味着Listtypes必须是Integer或Integer的超types。 genericstypes的边界超出了我想要详细说明的范围。 简而言之,它允许你定义一个通用types可以的types。 这使得可以多态地处理generics。 例如:
public void foo(List<? extends Number> numbers) {...}
您可以传递一个List<Integer>
, List<Float>
, List<Byte>
等numbers
。 没有types边界,这是行不通的 – 这就是generics。
最后,这里有一个方法定义,它使用通配符来做一些我认为你不能做的事情:
public static <T extends Number> void adder(T elem, List<? super Number> numberSuper) { numberSuper.add(elem); }
numberSuper
可以是数字列表或任何数字的超types(例如List<Object>
), elem
必须是Number或任何子types。 通过所有的边界,编译器可以确定.add()
是types安全的。
typesvariables<T>可以是您指定的任何非基本types:任何类types,任何接口types,任何数组types或甚至另一个typesvariables。
最常用的types参数名称是:
- 电子元素(广泛用于Java集合框架)
- K – 键
- N – 数字
- T型
- V – 值
在Java 7中,它允许像这样实例化:
Foo<String, Integer> foo = new Foo<>(); // Java 7 Foo<String, Integer> foo = new Foo<String, Integer>(); // Java 6