Javagenerics的“?”,“E”和“T”有什么区别?

我遇到这样的Java代码:

public interface Foo<E> {} public interface Bar<T> {} public interface Zar<?> {} 

上述三者之间的区别是什么?他们在Java中将这种types或接口声明称为什么?

那么前两者之间没有区别 – 他们只是使用不同的名称作为types参数ET )。

第三个不是有效的声明 – ? 用作提供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。 没有任何限制( extendssuper ),通配符意味着“在这里使用任何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