泛型(generic types)
泛型就是被参数化了类型的一个类或者接口。下面的Box类作为一个例子去理解这个概念
一个简单的Box类
先从一个非泛化的类Box开始,这个类作用于一个任何类型的对象。这里只需要两个方法,set 和 get
public class Box{
private Object object;
public void set(Object object){this.object = object;}
public Object get(){return this.object;}
}
你可以接受和返回除了原始类型(primitive type)以外的任何类型的对象。在运行期间无法验证类如何被使用。某一部分的代码可能放了一个Integer在Box里,且期望得到输出Integer,但是另外一部分代码可能错误的传入了一个String,结果在运行时产生错误。
Box类的泛型
一个泛化类(generic class)定义成如下形式:
类型参数(type parameter)部分被尖括号包围且在类名之后。详细点,就是 T1,T2,..,Tn。 这些叫做类型参数(type parameter)或者类型变量(type variables)
为了使用泛化来更新一下Box类,你可以创建一个泛型申明通过更改代码“public class Box”为"public class Box"。这里就使用了类型变量(type variables),可以用在类里的任何地方。
/**
* Generic version of the Box class.
* @param the type of the value being boxed
*/
public class Box {
// T stands for "Type"
private T t;
public void set(T t) { this.t = t; }
public T get() { return t; }
}
可以看到,所有的object都替换成了T。类型变量可以时任何非原始类型(non-primitive type),比如任何的class type,interface type,array type甚至其他的type variable
类型参数命名转换(Type parameter naming Conventions)
类型参数的名字都是单个字母,大写。
经常使用的类型参数的名字如下:
- 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
调用和实例化泛型(invoking and Instantiating a Generic Type)
在你的代码里想要引用Box泛化类,你需要调用泛型,也就是把T替换成一个具体的值,比如Integer
你可以把调用泛型接近当成是普通方法的调用,但是你不需要传入一个argument,而是传入一个type argument给Box类。
Type Parameter 和 Type Argument 的区别
Type parameter 和 Type Argument是不同的,编码的时候传入一个type argument是为了创建一个参数化类型(parameterized type). 所以 Foo中的T是一个类型参数(type parameter), Foo中里的String是一个type argument。
(所以 Foo就是一个parameterized type)
就像其他的变量声明一样,这里实际上并没有创建一个Box对象,而只是声明了说,integerBox会引用一个Integer的Box。
去实例化这个类,使用new关键字。但是需要把放在类名和括号之间。
Box integerBox = new Box();
Java 7之后,可以直接写成
Box integerBox = new Box<>();
编译器会自动从context中推断需要的type argument。new Box<>()里的<>被称为diamond.
多个类型参数(multiple Type Parameters)
一个泛化类可以有多个类型参数,比如OrderedPair泛化类,实现了Pair泛化接口
public interface Pair{
public K getKey();
public V getValue();
}
public class OrderedPair implements Pair{
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;}
}
下面的两句代码实例化了两个 OrderedPair 类:
Pair p1 = new OrderedPair("Even",8);
Pair p2 = new OrderedPair("Hello","world");
代码中,new OrderedPair把K实例化成String,把V 实例化成Integer。所以,OrderedPair的构造函数的参数类型就是String和Integer。由于自动打包,可以传入一个String和一个int。
刚刚提到了diamond,因为java编译器能够从OrderedPair推断出K 和V 的类型,所以使用diamond可以把代码缩写成:
Pair p1 = new OrderedPair<>("Even",8);
Pair p2 = new OrderedPair<>("Hello","world");
创建一个泛化接口和泛化类一样。
参数化类型(Parameterized Type)
你也可以使用一个参数化类型(parameterized)来替换类型参数(type parameter)。比如用List替换K
OrderedPair> p = new OrderedPair<>("primes", new Box(...));