理解Java泛型

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.在扩展的泛型类中使用桥接方法来保留多态性。

 
 

                
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值