Documentation

The Java™ Tutorials
Hide TOC
Generic Types泛型类型
Trail: Learning the Java Language
Lesson: Generics (Updated)

Generic Types泛型

A generic type is a generic class or interface that is parameterized over types. 泛型类型是通过类型参数化的泛型类或接口。The following Box class will be modified to demonstrate the concept.下面的Box类将被修改以演示该概念。

A Simple Box Class一个简单的Box类

Begin by examining a non-generic Box class that operates on objects of any type. 首先检查对任何类型的对象进行操作的非泛型Box类。It needs only to provide two methods: set, which adds an object to the box, and get, which retrieves it:它只需要提供两个方法:set(向框中添加对象)和get(检索对象):

public class Box {
    private Object object;

    public void set(Object object) { this.object = object; }
    public Object get() { return object; }
}

Since its methods accept or return an Object, you are free to pass in whatever you want, provided that it is not one of the primitive types. 因为它的方法接受或返回一个Object,所以只要它不是基本类型之一,您就可以自由地传入任何您想要的内容。There is no way to verify, at compile time, how the class is used. 在编译时无法验证类的使用方式。One part of the code may place an Integer in the box and expect to get Integers out of it, while another part of the code may mistakenly pass in a String, resulting in a runtime error.代码的一部分可能会将Integer放入框中并期望从中获取Integer,而代码的另一部分可能会错误地传入String,从而导致运行时错误。

A Generic Version of the Box ClassBox类的泛型版本

A generic class is defined with the following format:泛型类的定义格式如下:

class name<T1, T2, ..., Tn> { /* ... */ }

The type parameter section, delimited by angle brackets (<>), follows the class name. 类型参数部分,由尖括号分隔(<>),跟随类名。It specifies the type parameters (also called type variables) T1, T2, ..., and Tn.它指定类型参数(也称为类型变量)T1T2、…、和Tn

To update the Box class to use generics, you create a generic type declaration by changing the code "public class Box" to "public class Box<T>". 要更新Box类以使用泛型,可以通过将代码public class Box更改为public class Box<T>来创建泛型类型声明This introduces the type variable, T, that can be used anywhere inside the class.这引入了类型变量T,它可以在类中的任何位置使用。

With this change, the Box class becomes:通过此更改,Box类变为:

/**
 * Generic version of the Box class.
 * @param <T> the type of the value being boxed
 */
public class Box<T> {
    // T stands for "Type"
    private T t;

    public void set(T t) { this.t = t; }
    public T get() { return t; }
}

As you can see, all occurrences of Object are replaced by T. 如您所见,Object的所有引用均替换为TA type variable can be any non-primitive type you specify: any class type, any interface type, any array type, or even another type variable.类型变量可以是您指定的任何非基元类型:任何类类型、任何接口类型、任何数组类型,甚至其他类型变量。

This same technique can be applied to create generic interfaces.同样的技术也可以应用于创建通用接口。

Type Parameter Naming Conventions类型参数命名约定

By convention, type parameter names are single, uppercase letters. 按照惯例,类型参数名称是单个大写字母。This stands in sharp contrast to the variable naming conventions that you already know about, and with good reason: Without this convention, it would be difficult to tell the difference between a type variable and an ordinary class or interface name.这与您已经知道的变量命名约定形成了鲜明的对比,并且有很好的理由:没有这种约定,很难区分类型变量和普通类或接口名称之间的区别。

The most commonly used type parameter names are:最常用的类型参数名称有:

You'll see these names used throughout the Java SE API and the rest of this lesson.您将在JavaSEAPI和本课程的其余部分中看到这些名称的使用。

Invoking and Instantiating a Generic Type调用和实例化泛型类型

To reference the generic Box class from within your code, you must perform a generic type invocation, which replaces T with some concrete value, such as Integer:要从代码中引用泛型Box类,必须执行泛型类型调用,该调用将T替换为一些具体值,例如Integer

Box<Integer> integerBox;

You can think of a generic type invocation as being similar to an ordinary method invocation, but instead of passing an argument to a method, you are passing a type argumentInteger in this case — to the Box class itself.您可以将泛型类型调用视为与普通方法调用类似,但不是将参数传递给方法,而是传递类型参数—在本例中为Integer—到Box类本身。


Type Parameter and Type Argument Terminology:类型参数和类型形参术语: Many developers use the terms "type parameter" and "type argument" interchangeably, but these terms are not the same. 许多开发人员交替使用术语“类型参数”和“类型形参”,但这些术语并不相同。When coding, one provides type arguments in order to create a parameterized type. Therefore, the T in Foo<T> is a type parameter and the String in Foo<String> f is a type argument. 因此,TFoo<T>是类型参数,而Foo<String> f中的String是一个类型形参。This lesson observes this definition when using these terms. 本课程在使用这些术语时遵循此定义。

Like any other variable declaration, this code does not actually create a new Box object. 与任何其他变量声明一样,此代码实际上并不创建新的Box对象。It simply declares that integerBox will hold a reference to a "Box of Integer", which is how Box<Integer> is read.它只是声明integerBox将保存对“整型盒”的引用,这是Box<Integer>所能读到的。

An invocation of a generic type is generally known as a parameterized type.泛型类型的调用通常称为参数化类型

To instantiate this class, use the new keyword, as usual, but place <Integer> between the class name and the parenthesis:要实例化此类,请像往常一样使用new关键字,但在类名和括号之间放置<Integer>

Box<Integer> integerBox = new Box<Integer>();

The Diamond钻石

In Java SE 7 and later, you can replace the type arguments required to invoke the constructor of a generic class with an empty set of type arguments (<>) as long as the compiler can determine, or infer, the type arguments from the context. 在Java SE 7及更高版本中,可以用一组空的类型参数替换调用泛型类的构造函数所需的类型参数(<>)只要编译器能够从上下文中确定或推断类型参数。This pair of angle brackets, <>, is informally called the diamond. 这对尖括号,被非正式地称为钻石For example, you can create an instance of Box<Integer> with the following statement:例如,您可以创建Box<Integer>,声明如下:

Box<Integer> integerBox = new Box<>();

For more information on diamond notation and type inference, see Type Inference.有关菱形表示法和类型推断的更多信息,请参阅类型推断

Multiple Type Parameters多类型参数

As mentioned previously, a generic class can have multiple type parameters. 如前所述,泛型类可以有多个类型参数。For example, the generic OrderedPair class, which implements the generic Pair interface:例如,实现泛型Pair接口的泛型OrderedPair类:

public interface Pair<K, V> {
    public K getKey();
    public V getValue();
}

public class OrderedPair<K, V> implements Pair<K, V> {

    private K key;
    private V value;

    public OrderedPair(K key, V value) {
	this.key = key;
	this.value = value;
    }

    public K getKey()	{ return key; }
    public V getValue() { return value; }
}

The following statements create two instantiations of the OrderedPair class:以下语句创建OrderedPair类的两个实例化:

Pair<String, Integer> p1 = new OrderedPair<String, Integer>("Even", 8);
Pair<String, String> p2 = new OrderedPair<String, String>("hello", "world");

The code, new OrderedPair<String, Integer>, instantiates K as a String and V as an Integer. 代码new OrderedPair<String, Integer>K实例化为String,将V实例化为IntegerTherefore, the parameter types of OrderedPair's constructor are String and Integer, respectively. 因此,OrderedPair构造函数的参数类型分别为StringIntegerDue to autoboxing, it is valid to pass a String and an int to the class.由于自动装箱,将Stringint传递给类是有效的。

As mentioned in The Diamond, because a Java compiler can infer the K and V types from the declaration OrderedPair<String, Integer>, these statements can be shortened using diamond notation:正如钻石中提到的,因为Java编译器可以从声明OrderedPair<String, Integer>推断KV的类型,所以这些语句可以使用钻石符号缩短:

OrderedPair<String, Integer> p1 = new OrderedPair<>("Even", 8);
OrderedPair<String, String> p2 = new OrderedPair<>("hello", "world");

To create a generic interface, follow the same conventions as for creating a generic class.要创建泛型接口,请遵循与创建泛型类相同的约定。

Parameterized Types参数化类型

You can also substitute a type parameter (that is, K or V) with a parameterized type (that is, List<String>). 您还可以用参数化类型(即List<String>)替换类型参数(即KV)。For example, using the OrderedPair<K, V> example:例如,使用OrderedPair<K, V>例子:

OrderedPair<String, Box<Integer>> p = new OrderedPair<>("primes", new Box<Integer>(...));

Previous page: Why Use Generics?
Next page: Raw Types