您的位置:首页 > 编程语言 > Java开发

理解Java泛型

2014-04-28 11:03 351 查看
1.为什么要用泛型(generics)?

1)在编译时纠错;2)省去了转型;3)可以使用泛型方法。

2.泛型类型

泛型类型是类或接口,这些类或接口的类型是参数化(parameterized)被称为类型参数(type parameters),它对应的实参是类型(type),而普通参数(formal parameters)对应的实参是值(value)。

3.泛型类

定义:class ClassName<T1, T2, ..., Tn> { ... }

尖括号里的T1, T2, ..., Tn就是类型参数。

实例化泛型类:ClassName<type> objectName;

这里的type是类型实参(type argument),如类类型、接口类型、数组类型,但是不能是基本数据类型。

例子:

public class Box<T> {
   // T stands for "Type"
    privateT t;
    public void set(T t) { this.t = t; }
    publicT get() { return t; }


public class BoxDemo {
   public static void main(String []args){
        Box<Integer> integerBox = newBox<Integer>;
        integerBox.add(new Integer(10));
        Integer someInteger = integerBox.get();//不需要转型
        System.out.println(someInteger);}
}


4.泛型方法

和型类类似,只是作用于局限在声明它的方法中。

public class Box<T> {
 
   private T t;         
 
   public void set(T t) {
       this.t = t;
    }
 
   public T get() {
       return t;
    }
 
   public <U> void inspect(U u){
       System.out.println("T: " + t.getClass().getName());
       System.out.println("U: " + u.getClass().getName());
    }
 
   public static void main(String[] args) {
       Box<Integer> integerBox = new Box<Integer>();
       integerBox.set(new Integer(10));
       integerBox.inspect("some text");
    }
}


在这里inspect 为泛型方法,它定义一个名为U的类型参数,这个方法接受一个对象,把对象的类型发送到标准输出。该程序的结果:

T: java.lang.Integer

U: java.lang.String

5.受限类型参数(bounded typeparameter)

有时候我们需要限制传递给类型参数的类型种类,例如对数字进行操作的方法可能只希望接受Number或其子类的实例。这就是受限类型参数的用途。

用法:<T extends UpperBound>,UpperBound指的是上限。

public class Box<T> {
 
    privateT t;         
 
   public void set(T t) {
       this.t = t;
    }
 
   public T get() {
       return t;
    }
 
   public <U extends Number> void inspect(U u){
       System.out.println("T: " + t.getClass().getName());
       System.out.println("U: " + u.getClass().getName());
    }
 
   public static void main(String[] args) {
       Box<Integer> integerBox = new Box<Integer>();
       integerBox.set(new Integer(10));
       integerBox.inspect("some text"); // 出错!实参是String不是Number。
    }
}


前一小节的例子中inspect的实参可以使任何类型的(除基本数据类型),但是这里的例子中inspect的实参只能是Number极其子类型,否则会变异失败。

一个类型参数也可以有多个上限,使用&连接,但是要注意上限是类名的必须放在前面。

Class A { /* ... */ }

interface B { /* ... */ }

interface C { /* ... */ }

class D <T extends A & B & C>{ /* ... */ }



6.通配符

在泛型中,通配符“?”表示未知类型。

1)非受限的通配符“<?>”

它一般指两种情况下使用:

i.方法是通过Object的函数实现的。

ii.泛型类的方法不依赖于类型参数,例如 List.size或List.clear。

public static void printList(List<?>list) {
   for (Object elem: list)
       System.out.print(elem + "");
   System.out.println();
}


上面的例子中,对于任意的特定类型A,list<A>都是list<?>的子类型,所以可以用printList打印任何类型。

List<Integer> li = Arrays.asList(1,2, 3);
List<String>  ls = Arrays.asList("one","two", "three");
printList(li);
printList(ls);


这里Arrays.asList方法表示将特定的数组转换成特定大小的List。

2)有上限的通配符“<?extends A>”

public static double sumOfList(List<?extends Number> list) {
   double s = 0.0;
   for (Number n : list)
       s += n.doubleValue();
   return s;
}


List<Integer> li = Arrays.asList(1,2, 3);
System.out.println("sum = " +sumOfList(li));


输出结果是6.0。

3)有下限的通配符“<? super A>”

下面的程序将1~10添加到List中:

public static void addNumbers(List<?super Integer> list) {
   for (int i = 1; i <= 10; i++) {
       list.add(i);
    }
}




5.类型擦出(type erasure)

虚拟机中并没有泛型类型对象,所有的对象都是一样的,都属于普通的类。由于JVM根本不支持泛型类型,是编译器“耍了个花招”,使得似乎存在对泛型类型的支持——它们用泛型类型信息检查所有的代码,但随即“擦除”所有的泛型类型并生成只包含普通类型的类文件。泛型类在Java源码上看起来与一般的类不同,在执行时被虚拟机翻译成对应的“原始类型”。泛型类的类型参数列表被去掉,虚拟机用类型参数的限定类型对使用类型参数的地方进行了替换,如果没有限定类型则使用Object类型进行替换。这个过程就是所谓的“类型擦除”。类型参数如果有多个限定,则使用第一个限定类型做替换。泛型方法也会做相同的替换。(摘自nothing0318的博客)

java编译器实现类型擦除的过程:

i.将类型参数用他们的上限或下限代替,没有上下限是用Object代替。

ii.如果需要的话使用转型来保证类型安全

iii.在扩展的泛型类中使用桥接方法来保留多态性。




                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: