Java泛型简介

问题

 
public class ArrayList {
    public Object get(int i) { ... }
    public void add(Object o) { ... }
    ...
    private Object[] elementData;
}
​当没有泛型时,我们会使用obejct替代。但这样会存在以下问题
第一有关get方法:每次调用get方法都会返回一个Object对象,每次都要强制类型转换为我们需要的类型。
第二有关add方法:假如我们往聚合了String对象的ArrayList中加入一个File对象,编译器不会产生任何错误提示
 
为了解决上面的问题,范型应运而生。
 
 

泛型类

 
Pair<String, Integer> pair = new Pair<String, Integer>();​
 
你可能想象 List<Integer> 代表一个 E 被全部替换成 Integer 的版本。它可能导致误解,因为泛型声明绝不会实际的被这样替换。没有代码的多个拷贝。这是和C++模板的很大的区别。
 
如果 Foo Bar 的一个子类型 ,而 G 是泛型声明,那么 G<Foo> G<Bar> 的子类型并不成立
 
 

泛型方法

 

static <T> void fromArrayToCollection(T[] a, Collection<T> c){
       for (T o : a) {
           c.add(o); // correct
       }
}

 

编译器根据实参为我们 推断类型参数的值 。它通常推断出能使调用类型最明确的类型参数
 
class Collections {

public static <T>  void copy(List<T> dest, List<? extends T> src){...}

}

class Collections {

public static <T, S extends T>  void copy(List<T> dest, List<S> src){...}

}

<T> T writeAll(Collection<T> coll, Sink<? super T> snk) { … }

String str = writeAll(cs, s); // YES!!!

推断出来的T是String。

 
 

通配符

 
Collection<?>。它的元素类型可以匹配任何类型。
 

Collection<?> c = new ArrayList<String>();

c.add(new Object()); // 编译时错误

因为我们不知道c的元素类型,我们不能向其中添加对象。另一方面,我们可以调用get()方法并使用其返回值。

返回值是一个未知的类型,但是我们知道,它总是一个Object,因此把get的返回值赋值给一个Object类型的对象。

 

通配符的上限(upper bound)

 
public  void drawAll(List<?  extends Shape> shapes) {  //..}
这里?代表一个未知的类型,就像我们前面看到的通配符一样。但是,在这里,我们知道这个未知的类型实际上是Shape的一个子类

 

public void addRectangle(List<? extends Shape> shapes) {

   shapes.add(0, new Rectangle()); // compile-time error!

}

你应该能够指出为什么上面的代码是不允许的。因为shapes.add的第二个参数类型是extends Shape ——一个Shape未知的子类。因此我们不知道这个类型是什么,所以这里传递一个Rectangle不安全。

 

如果你有一个只使用类型参数 T 作为参数的 API ,它的使用应该利用下限通配符 ( ? super T ) 的好处。
如果 API 只返回 T ,你应该使用上限通配符 ( ? extends T ) 来给你的客户端更大的灵活性。

​​可以使用&将多个通配符链接
 
 

新老代码兼容(java5为分界线)

 
当一个泛型类型,比如 Collection 被使用而没有类型参数时,它被称作一个 raw type( 自然类型 ??)。 类型 Collection 表示一个未知类型元素的集合,就像 Collection<?>
 
自然类型和通配符类型很像,但是他们的类型检查不是同样严格。允许泛型与已经存在的老代码相交互是一个深思熟虑的决定。
 
一旦你把泛型编程和非泛型编程混合起来,泛型系统所提供的所有安全保证都失效。然而,你还是比你根本不用泛型要好。至少你知道 你这一端的代码是 稳定 的。
 
 

本质(编译和擦除)

 

 

public String loophole(Integer x) {
       List<String> ys = new LinkedList<String>();
       List xs = ys;
       xs.add(x); // compile-time unchecked warning
       return ys.iterator().next();

}

上面的代码与下面的代码的行为一样:

public String loophole(Integer x) {
       List ys = new LinkedList();
       List xs = ys;
       xs.add(x);
       return (String) ys.iterator().next(); // run time error
}
 

 

泛型是通过 java 编译器的称为擦除 (erasure) 的前端处理来实现的。你可以(基本上就是)把它认为是一个从源码到源码的转换,它把泛型版本的 loophole() 转换成非泛型版本。
 
 

List<String> l1 = new ArrayList<String>();

List<Integer> l2 = new ArrayList<Integer>();

System.out.println(l1.getClass() == l2.getClass());//true

所有的泛型类型在运行时有同样的类 (class) ,而不管他们的实际类型参数
 
 

Collection cs = new ArrayList<String>();

if (cs instanceof Collection<String>) { ...} // 非法

类似的,如下的类型转换

Collection<String> cstr = (Collection<String>) cs;

得到一个unchecked warning,因为运行时环境不会为你作这样的检查。

对类型变量也是一样:

<T> T badCast(T t, Object o) {

    return (T) o; // unchecked warning

}

类型参数在运行时并不存在。这意味着它们不会添加任何的时间或者空间上的负担,这很好。不幸的是,这也意味着你不能依靠他们进行类型转换。
 
 
String.class 类型代表  Class<String>
 
public static <T extends Comparable> T min(T[] a)

编译后经过类型擦除会变成下面这样:

public static Comparable min(Comparable[] a)
 
 

注意事项

 
不能用基本类型实例化类型参数(new Pair<intint>()
不能抛出也不能捕获泛型类实例(T t;throw t;)
参数化类型的数组不合法( new Pair<String, String>[10] )
不能实例化类型变量(new T(...) )
​​E(element)、K(key)、V(value)
 
 
 

参考​​

​​ht​tp://blog.csdn.net/explorers/article/details/454837
http://www.cnblogs.com/absfree/p/5270883.html
 
 
 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值