泛型类型是经过类型参数化的泛型类或接口,修改如下Box
类以演示此概念。segmentfault
首先检查一个对任何类型的对象进行操做的非泛型Box
类,它只须要提供两个方法:set
,它将一个对象添加到box中,get
,它将检索它:数组
public class Box { private Object object; public void set(Object object) { this.object = object; } public Object get() { return object; } }
因为它的方法接受或返回一个Object
,因此你能够自由地传入任何你想要的东西,前提是它不是一种原始类型,没法在编译时没有办法验证如何使用该类,代码的一部分可能会在box中放置一个Integer
并指望从中获取Integer
,而代码的另外一部分可能会错误地传入String
,从而致使运行时错误。框架
泛型类使用如下格式定义:函数
class name<T1, T2, ..., Tn> { /* ... */ }
由尖括号(<>
)分隔的类型参数部分跟在类名后面,它指定类型参数(也称为类型变量)T1, T2, ...
和Tn
。this
要更新Box
类以使用泛型,能够经过将代码“public class Box
”更改成“public class Box <T>
”来建立泛型类型声明,这引入了类型变量T
,能够在类中的任何位置使用。编码
经过此更改,Box
类变为:code
/** * 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
替换,类型变量能够是你指定的任何非基本类型:任何类类型、任何接口类型、任何数组类型,甚至是其余类型变量。对象
能够应用相同的技术来建立泛型接口。接口
按照惯例,类型参数名称是单个大写字母,这与你已经了解的变量命名约定造成鲜明对比,而且有充分的理由:若是没有这种约定,就很难区分类型变量和普通类或接口名称。开发
最经常使用的类型参数名称是:
你将在整个Java SE API和本课程的其他部分中看到这些名称。
要从代码中引用泛型Box
类,必须执行泛型类型调用,它将T
替换为某些具体值,例如Integer
:
Box<Integer> integerBox;
你能够将泛型类型调用视为与普通方法调用相似,但不是将参数传递给方法,而是将类型参数(在本例中为Integer
)传递给Box
类自己。
类型参数和类型参数术语:许多开发人员互换地使用术语“类型参数”和“类型实参”,但这些术语并不相同,编码时,提供类型实参以建立参数化类型,所以,Foo<T>
中的T
是类型参数,而Foo<String> f
中的String
是类型实参,本课程在使用这些术语时会遵循此定义。
与任何其余变量声明同样,此代码实际上并不建立新的Box
对象,它只是声明integerBox
将保存对“Box of Integer”的引用,这就是Box<Integer>
的读取方式。
泛型类型的调用一般称为参数化类型。
要实例化此类,请像往常同样使用new关键字,但在类名和括号之间放置<Integer>
:
Box<Integer> integerBox = new Box<Integer>();
在Java SE 7及更高版本中,只要编译器能够从上下文中肯定或推断类型参数,就能够用一组空的类型参数(<>
)替换调用泛型类的构造函数所需的类型参数,这对尖括号<>
非正式地称为菱形,例如,你可使用如下语句建立Box<Integer>
的实例:
Box<Integer> integerBox = new Box<>();
有关菱形表示法和类型推断的更多信息,请参阅类型推断。
如前所述,泛型类能够有多个类型参数,例如,OrderedPair
泛型类,它实现了Pair
泛型接口:
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
类的两个实例:
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
,因为自动装箱,将String
和int
传递给类是有效的。
正如菱形中所提到的,由于Java编译器能够从声明OrderedPair<String, Integer>
推断出K
和V
类型,因此可使用菱形表示法缩短这些语句:
OrderedPair<String, Integer> p1 = new OrderedPair<>("Even", 8); OrderedPair<String, String> p2 = new OrderedPair<>("hello", "world");
要建立泛型接口,请遵循与建立泛型类相同的约定。
你还可使用参数化类型(即List<String>
)替换类型参数(即K
或V
),例如,使用OrderedPair<K, V>
示例:
OrderedPair<String, Box<Integer>> p = new OrderedPair<>("primes", new Box<Integer>(...));