繁体   English   中英

“E”、“T”和“?”有什么区别? 对于 Java generics?

[英]What is the difference between 'E', 'T', and '?' for Java generics?

我遇到这样的 Java 代码:

public interface Foo<E> {}

public interface Bar<T> {}

public interface Zar<?> {}

以上三者之间有什么区别,他们将这种类型的 class 或 Java 中的接口声明称为什么?

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

第三个不是有效的声明 - ? 用作提供类型参数时使用的通配符 ,例如List<?> foo = ...表示foo引用某种类型的列表,但我们不知道是什么。

所有这些都是泛型 ,这是一个非常大的话题。 您可能希望通过以下资源了解它,尽管当然有更多可用的资源:

它比其他任何东西都更常见。

  • T意味着是一种类型
  • E意味着是一个元素( List<E> :元素列表)
  • K是Key(在Map<K,V>
  • V是值(作为返回值或映射值)

它们完全可以互换(尽管存在相同声明中的冲突)。

前面的答案解释了类型参数(T,E等),但不解释通配符,“?”或它们之间的差异,所以我将解决这个问题。

首先,要明确:通配符和类型参数不一样。 其中类型参数定义了一种表示范围类型的变量(例如,T),通配符不是:通配符只定义了一组可用于泛型类型的允许类型。 没有任何边界( extendssuper ),通配符意味着“在这里使用任何类型”。

通配符总是位于尖括号之间,它只在泛型类型的上下文中有意义:

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)  {...}

那么,如果有重叠,为什么要使用其中一个呢? 有时,它实际上只是风格:有人说如果你不需要类型参数,你应该使用通配符来使代码更简单/更易读。 我在上面解释了一个主要区别:type params定义了一个类型变量(例如,T),你可以在范围的其他地方使用它; 通配符没有。 否则,类型参数和通配符之间有两个很大的区别:

类型params可以有多个边界类; 通配符不能:

public class Foo <T extends Comparable<T> & Cloneable> {...}

通配符可以有下限; 类型参数不能:

public void bar(List<? super Integer> list) {...}

在上面的List<? super Integer> 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 of Number或任何超类型的Number(例如, List<Object> ), elem必须是Number或任何子类型。 通过所有边界,编译器可以确定.add()是类型安全的。

类型变量<T>可以是您指定的任何非基本类型:任何类类型,任何接口类型,任何数组类型,甚至是其他类型变量。

最常用的类型参数名称是:

  • E - Element(Java Collections Framework广泛使用)
  • 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
T - Type
V - Value
S,U,V etc. - 2nd, 3rd, 4th types

您将在整个Java SE API中看到这些名称

当编译一个函数时,编译器将对每个通配符 (例如,List中的问号)进行捕获

foo(List<?> list) {
    list.put(list.get()) // ERROR: capture and Object are not identical type.
}

但是像V这样的泛型类型可以使它成为通用方法

<V>void foo(List<V> list) {
    list.put(list.get())
}

<?>表示“任何”。

<T>表示“特定类型”。

<E>表示“元素”,常用于 Java Collections 框架中。

假设我们有两个类:

   public class DataType<E>{
    private E data;
    private DataType<E> next;
// constructor, getters, setters, toString();

}

public class LinkedListDemo{
public static void main(String[] args){
DataType<Integer> type1 = new DataType<Integer>(5, null);
DataType<Number> type2 = new DataType<>(Double.valueOf(23.4), null);
type2.setNext(type2);
}

在第二个 class 中,行type2.setNext(type2)将不起作用,因为对于 type1,我们将<Integer>作为参数化类型,对于 type2,我们将<Number>作为参数化类型。 <E>的角度来看,这是不可能的。

但是当我们在这一行写问号<?>

private DataType<?> next; 

此错误消失。 带有问号<?>它可以是任何类型。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM