泛型类型

Author Avatar
贾康 11月 11, 2016

#泛型类型
泛型类型是一个参数化的普通的类或者接口。以下我们通过修改Box类来说明这点。

###一个简单的Box类
我们从一个非泛型的Box类开始。Box类可以操作任何任何类型的对象。它只需要提供两个方法,set用来向Box中增加一个对象和get用来检索这个对象。

1
2
3
4
5
6
public class Box {
private Object object;
public void set(Object object) { this.object = object; }
public Object get() { return object; }
}

既然Box的方法接受或者返回的都是Object。那么你可以传递任何非原生类型的参数。我们在编译时没有办法确定这个类是如何被使用的。一部分代码可能在这个类中放入一个Integer,并且期望取出一个Integer,但是另一部分代码可能错误的将取出的Integer转为String,结果就是出现了运行时异常。

###Box类的泛型版本
一个泛型类可以通过如下的形式定义:

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

类名后边被尖括号括起来的部分就是类型参数。它声明了类型参数T1,T2…Tn.

为了使Box类使用泛型,我们可以通过改变类声明为“public class Box”来创建一个泛型类型声明。这就声明了类型变量T,它可以被用在Box类的各个部分。

经过这些变化,Box类变成了下面这样:

1
2
3
4
5
6
7
8
9
10
11
/**
* 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; }
}

如你所见,所有Object都被替换成了T,一个类型参数可以是任何非原生的类型,包括类,接口,或是数组甚至是其他的类型变量。
和以上相同的技术也可以被用来创建泛型接口。

###类型参数命名习惯
一般来说,类型参数的名字是单个大写字母,这和变量的命名形成了鲜明的对比。这么做的原因是:如果不这样,区分类型变量和普通类的名字将会十分复杂。
最常用类型参数名字如下所示:

  • E Element
  • K Key
  • N number
  • T Type
  • V value
  • S,U,V etc 第二三四…个类型。

这些名字被广泛使用在Java SE API和课程的其他部分中。

###调用和实例化一个泛型类型
想要得到一个Box类的引用,你必须进行泛型类型的调用,在调用中你需要将T替换成一些确定的值如Integer。

1
Box<Integer> integerBox;

你可以将泛型类型调用和普通的参数调用类比,但是不同于提供一个变量给这个方法,你传递了一个类型变量给了Box 类。

类型参数和类型变量:很多开发者将这两个概念互换,然而他们并不相同。Foo中的T是类型参数,而Foo中的String是类型变量。

像其他变量声明一样,以上的代码并没有创建一个新的Box对象,他只是声明了integerBox将存放一个‘存放Integer的Box’的引用。这就是Box的含义。
一个泛型类型的调用一般被称作参数化类型。

要实例化这个类,使用new关键字。但是我们需要把放在类名和括号之间。

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

TIPS:从java7开始,我们可以这样写了:

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

###多类型参数
正如之前提及的那样,一个泛型类可以有多个类型参数。举个例子:OrderedPair类,它实现了Pair接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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; }
}

以下的代码创建了两个OrderedPair类的实例:

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

代码new OrderedPair < String, Integer >将K实例化为String,V实例化为Integer。因此,OrderedPair的构造函数的参数类型为String,和Integer。此外,因为自动装箱技术。我们可以传入int作为参数。

另外根据tips,我们可以写作:

1
OrderedPair<String, Integer> p1 = new OrderedPair<>("Even", 8);

创建泛型接口和创建泛型类的方法相同。

###泛型参数作为参数类型
你也可以将类型参数替换为泛型类。以OrderedPair 为例:

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

下一页