首页 > 代码库 > Java 泛型
Java 泛型
泛型的作用
使用泛型机制编写的程序代码要比那些杂乱地使用Object变量,然后再进行强制类型转换的代码具有更好的安全性和可读性.泛型对于集合类尤其有用.
说白了就是减少类型转换,增加可读性,同时避免代码中大量的Objcet的使用带来的转换安全隐患。
简单泛型类的定义
一个泛型类(generic class)就是具有一个或多个类型变量的类.下面是Pair类的代码:
- public class Pair<T>
- {
- private T first;
- private T second;
- public Pair(){ first = null; second = null;}
- public Pair(T first, T second) { this.first = first; this.second = second; }
- public T getFirst() { return first; }
- public T getSecond() { return second; }
- public void setFirst(T newValue) { first = newValue; }
- public void setSecond(T newValue) { second = newValue; }
- }
Pair类引入了一个类型变量T,用尖括号<>括起来,并放在类名的后面.泛型类可以有多个类型变量.例如,可以定义Pair类,其中第一个域和第二个域使用不同的类型:
- public class Pair<T, U> { ... }
类定义中的类型变量指定方法的返回类型以及域和局部变量的类型.例如:
- private T first; // uses the type variable
注释:类型变量使用大写形式,且比较短,这是很常见的.在Java库中,使用变量E表示集合的元素类型,K和V分别表示表的关键字与值的类型. T(需要时还可以用临近的字母U和S)表示"任意类型".
用具体的类型替换类型变量就可以实例化泛型类型,例如:
- Pair<String>
可以将结果想象成带有构造器的普通类:
- Pair<String>()
- Pair<String>(String, String)
和方法:
- String getFirst()
- String getSecond()
- void setFirst(String)
- void setSecond(String)
换句话说,泛型类可以看做普通类的工厂.
泛型方法
- class ArrayAlg
- {
- public static <T> T getMiddle(T...a)
- {
- return a[a.length / 2];
- }
- }
这个方法是在普通类中定义的,而不是在泛型类中定义的.然而,这是一个泛型方法,可以从尖括号和类型变量看出这一点.注意,类型变量放在修饰符(这里是 public static)的后面,返回类型的前面.
泛型方法可以定义在普通类中,也可以定义在泛型类中.
当调用一个泛型方法时,在方法名前的尖括号中放入具体的类型:
- String middle = ArrayAlg.<String>getMiddle("John", "Q.", "Public");
在这种情况下,方法调用中可以省略<String>类型参数.编译器有足够的信息能够推断出所调用的方法.它用names的类型(即String[])与泛型类型T[]进行匹配并推断出T一定是String,也就是说,可以调用
- String middle = ArrayAlg.getMiddle("John", "Q.", "Public");
书上说在大多数情况下,对泛型方法的推断没有问题,但是,如果编译器无法推断出一个合适的类型,此时编译器就会报错。
如下:
- package com.zjf;
- public class Test {
- public static void main(String[] args) {
- test("zjf","xhj");
- test("zjf",11);
- test("zjf",11,3.14);
- test(11,3.14,0);
- }
- public static <T> void test(T... t)
- {
- }
- }
实际上,在我的环境下(java7),不仅编译没有报错,运行也没有问题。
我猜想因为他们都是属于Object的子类,再怎么推断不出来,也可以使用Object。
类型变量的限定
使用extends 进行限制
- public static <T extends Comparable> void test(T t1,T t2)
- {
- t1.compareTo(t2);
- }
这样能保证,T必然是Comparable的子类或者接口实现,在方法里面就可以对变量a的内容使用Comparable具有的方法。
一个类型变量或通配符可以有多个限定,例如:
T extends Comparable & Serializable
泛型代码和虚拟机
虚拟机没有泛型类型对象--所有对象都属于普通类.无论何时定义一个泛型类型,都自动提供了一个相应的原始类型(raw type).原始类型的名字就是删除类型参数后的泛型类型名.擦除(erased)类型变量,并替换为限定类型(无限定的变量用Object).
例如,Pair<T>的原始类型如下所示:
- public class Pair
- {
- private Object first;
- private Object second;
- public Pair(Object first, Object second)
- {
- this.first = first;
- this.second = second;
- }
- ...
- }
因为T是一个无限定的变量,所以直接用Object替换.
在程序中可以包含不同类型的Pair,例如,Pair<String>或Pair<GregorianCalendar> .而擦除类型后就变成原始的Pair类型了.
原始类型用第一个限定的类型变量来替换,如果没有给定限定就用Object替换.例如,类Pair<T>中的类型变量没有显式的限定,因此,原始类型用Object替换T .假定声明了一个不同的类型.
- public class Interval<T extends Comparable & Serializable> implements Serializable
- {
- private T lower;
- private T upper;
- ...
- public Interval(T first, T second)
- {
- if (first.compareTo(second) <= 0)
- {
- lower = first;
- upper = second;
- }
- else
- {
- lower = second;
- upper = second;
- }
- }
- }
原始类型Interval如下所示:
- public class Interval implements Serializable
- {
- private Comparable lower;
- private Comparable upper;
- ...
- public Interval(Comparable first, Comparable second)
- {...}
- }
翻译泛型表达式
当程序调用泛型方法时,如果擦掉返回类型,编译器插入强制类型转换.例如,下面这个语句序列
- Pair<Employee> buddies = ...;
- Employee buddy = buddies.getFirst();
擦除getFirst的返回类型后将返回Object类型.编译器自动插入Employee的强制类型转换.也就是说,编译器把这个方法调用翻译为两条虚拟机指令:
对原始方法Pair.getFirst的调用
将返回的Object类型强制转换为Employee类型
Java 泛型