我遇到过这样的Java代码:
public interface Foo<E> {}
public interface Bar<T> {}
public interface Zar<?> {}
上述三种类型之间有什么区别?在Java中,他们将这种类型的类或接口声明称为什么?
我遇到过这样的Java代码:
public interface Foo<E> {}
public interface Bar<T> {}
public interface Zar<?> {}
上述三种类型之间有什么区别?在Java中,他们将这种类型的类或接口声明称为什么?
当前回答
最常用的类型参数名是:
E - Element (used extensively by the Java Collections Framework)
K - Key
N - Number
R - Result
T - Type
V - Value
S,U,V etc. - 2nd, 3rd, 4th types
您将在整个Java SE API中看到这些名称
其他回答
好吧,前两者之间没有区别——它们只是为类型参数使用了不同的名称(E或T)。
第三个不是有效的声明- ?用作通配符,在提供类型参数时使用,例如List<?> foo =…意思是foo指向某个类型的列表,但我们不知道是什么类型。
所有这些都是泛型,这是一个相当大的主题。你可能希望通过以下资源了解它,当然还有更多可用的资源:
Java泛型教程 泛型语言指南 Java编程语言中的泛型 Angelika Langer的Java泛型常见问题解答(大量而全面;更多参考资料)
这比什么都要传统。
T代表一种类型 E是一个元素(List<E>:一个元素列表) K是Key(在Map<K,V>中) V是Value(作为返回值或映射值)
它们是完全可互换的(尽管在同一声明中存在冲突)。
类型变量<T>可以是您指定的任何非基元类型:任何类类型、任何接口类型、任何数组类型,甚至是另一个类型变量。
最常用的类型参数名是:
E - Element(被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
最常用的类型参数名是:
E - Element (used extensively by the Java Collections Framework)
K - Key
N - Number
R - Result
T - Type
V - Value
S,U,V etc. - 2nd, 3rd, 4th types
您将在整个Java SE API中看到这些名称
前面的答案解释了类型参数(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
or
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>等数字。如果没有类型边界,这将不起作用——这就是泛型。
最后,这里有一个方法定义,它使用通配符来做一些我认为你不能用其他方法做的事情:
public static <T extends Number> void adder(T elem, List<? super Number> numberSuper) {
numberSuper.add(elem);
}
numberSuper可以是Number的列表或Number的任何超类型(例如,List<Object>),而elem必须是Number或任何子类型。有了所有的边界,编译器可以确定.add()是类型安全的。