泛型

(一下 几乎都是摘抄 )

泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。 Java语言引入泛型的好处是安全简单。

在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的"任意化","任意化"带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。

泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,以提高代码的重用率。

规则限制


1、泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。

2、同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。

3、泛型的类型参数可以有多个。

4、泛型的参数类型可以使用extends语句,例如<T extends superclass>。习惯上称为"有界类型"。

5、泛型的参数类型还可以是通配符类型。例如Class<?> classType = Class.forName("java.lang.String");


集合中使用泛型:

集合可以存储任意类型的对象,但是当把一个对象存入集合后,集合会“忘记”这个对象的类型,将该对象从集合中取出时,这个对象的编译类型就变成了Object类型。如果进行强制性转换就很容易出错。

package study;
import java.util.*;
public class Example34 {
public static void main(String[] args) {
ArrayList<String> list=new ArrayList<String>();
list.add("String");
list.add("Collection");
for(String str:list){
System.out.println(str);
}
}
}

所有的强制转换都是自动和隐式

自定义泛型:泛型类和泛型方法

(这个有点复杂。。。算了,以后想看的话可以去)

(http://www.cnblogs.com/toSeeMyDream/p/6434696.html)

通配符类型

通配符“?”同样可以对类型进行限定。可以分为子类型限定、超类型限定和无限定。通配符不是类型变量,因此不能在代码中使用"?"作为一种类型。

 

子类型限定

表示类型的上界,类似泛型的类型变量限定,格式是:? extends X。

特点:

1、限定为X和X的子类型,但不知道具体是哪个子类型,可能是怕导致类型转换错误(比如向下转型时编译不会报错,但是如果超类引用不是指向子类对象,那么运行时会报错),所以作为方法的参数时,这里禁止传递特定的类型。

2、作为方法的参数时,可以传递null。

作用:主要用来安全地访问数据,可以访问X及其子类型。

超类型限定

 

表示类型的下界,格式是:? super X。

特点:

1、限定为X和X的超类型,直至Object类,因为不知道具体是哪个超类型,因此方法返回的类型只能赋给Object。

2、因为可以向上转型,所以作为方法的参数时,可以传递X以及X的子类型。

3、作为方法的参数时,可以传递null。

作用:主要用来安全地写入数据,可以写入X及其子类型。


(以下转述自:http://blog.csdn.net/zero__007/article/details/52245475)
看下面一段代码:
[java]  view plain  copy
  1. // public final class Integer extends Number  
  2. Number num = new Integer(1);    
  3. List<Number> list = new ArrayList<>();  
  4. list.add(new Integer(3));  
  5. ArrayList<Number> list = new ArrayList<Integer>(); //type mismatch  
  6.   
  7. List<? extends Number> list = new ArrayList<Number>();  
  8. list.add(new Integer(1)); //error  
为什么Number的对象可以由Integer实例化,而ArrayList<Number>的对象却不能由ArrayList<Integer>实例化?list中的<? extends Number>声明其元素是Number或Number的派生类,为什么不能add Integer?为了解决这些问题,需要了解Java中的逆变和协变以及泛型中通配符用法。

1. 逆变与协变

       Java中String类型是继承自Object的,姑且记做String ≦ Object,表示String是Object的子类型,String的对象可以赋给Object的对象。而Object的数组类型Object[],理解成是由Object构造出来的一种新的类型,可以认为是一种构造类型,记f(Object),那么可以这么来描述协变和逆变:
       当A ≦ B时,如果有f(A) ≦ f(B),那么f叫做协变;
       当A ≦ B时,如果有f(B) ≦ f(A),那么f叫做逆变;
       如果上面两种关系都不成立则叫做不可变。

2. 泛型中的通配符实现协变与逆变

       JAVA中泛型是不变的,可有时需要实现逆变与协变,怎么办呢?这时就需要通配符?。
       <? extends>实现了泛型的协变,比如:
[java]  view plain  copy
  1. List<? extends Number> list = new ArrayList<>();  
       “? extends Number”则表示通配符”?”的上界为Number,换句话说就是,“? extends Number”可以代表Number或其子类,但代表不了Number的父类(如Object),因为通配符的上界是Number。
       于是有“? extends Number” ≦ Number,则List<? extends Number> ≦ List< Number >。那么就有:
[java]  view plain  copy
  1. List<? extends Number> list001 = new ArrayList<Integer>();  
  2. List<? extends Number> list002 = new ArrayList<Float>();  
       但是这里不能向list001、list002添加除null以外的任意对象。可以这样理解一下,List<Integer>可以添加Interger及其子类,List<Float>可以添加Float及其子类,List<Integer>、List<Float>都是List<? extends Animal>的子类型,如果能将Float的子类添加到List<? extends Animal>中,就说明Float的子类也是可以添加到List<Integer>中的,显然是不可行。故java为了保护其类型一致,禁止向List<? extends Number>添加任意对象,不过却可以添加null。

       <? super>实现了泛型的逆变,比如:
[java]  view plain  copy
  1. List<? super Number> list = new ArrayList<>();  
       “? super Number” 则表示通配符”?”的下界为Number。为了保护类型的一致性,因为“? super Number”可以是Object或其他Number的父类,因无法确定其类型,也就不能往List<? super Number >添加Number的任意父类对象。但是可以向List<? super Number >添加Number及其子类。
[java]  view plain  copy
  1. List<? super Number> list001 = new ArrayList<Number>();  
  2. List<? super Number> list002 = new ArrayList<Object>();  
  3. list001.add(new Integer(3));  
  4. list002.add(new Integer(3));  

3.PECS

       现在问题来了:究竟什么时候用extends什么时候用super呢?《Effective Java》给出了答案:
       PECS: producer-extends, consumer-super.
       比如,一个简单的Stack API:
[java]  view plain  copy
  1. public class Stack<E>{  
  2.     public Stack();  
  3.     public void push(E e):  
  4.     public E pop();  
  5.     public boolean isEmpty();  
  6. }  
       要实现pushAll(Iterable<E> src)方法,将src的元素逐一入栈:
[java]  view plain  copy
  1. public void pushAll(Iterable<E> src){  
  2.     for(E e : src)  
  3.         push(e)  
  4. }  
       假设有一个实例化Stack<Number>的对象stack,src有Iterable<Integer>与 Iterable<Float>;在调用pushAll方法时会发生type mismatch错误,因为Java中泛型是不可变的,Iterable<Integer>与 Iterable<Float>都不是Iterable<Number>的子类型。因此,应改为
[java]  view plain  copy
  1. // Wildcard type for parameter that serves as an E producer  
  2. public void pushAll(Iterable<? extends E> src) {  
  3.     for (E e : src)  
  4.         push(e);  
  5. }  
       要实现popAll(Collection<E> dst)方法,将Stack中的元素依次取出add到dst中,如果不用通配符实现:
[java]  view plain  copy
  1. // popAll method without wildcard type - deficient!  
  2. public void popAll(Collection<E> dst) {  
  3.     while (!isEmpty())  
  4.         dst.add(pop());     
  5. }  
       同样地,假设有一个实例化Stack<Number>的对象stack,dst为Collection<Object>;调用popAll方法是会发生type mismatch错误,因为Collection<Object>不是Collection<Number>的子类型。因而,应改为:
[java]  view plain  copy
  1. // Wildcard type for parameter that serves as an E consumer  
  2. public void popAll(Collection<? super E> dst) {  
  3.     while (!isEmpty())  
  4.         dst.add(pop());  
  5. }  
       在上述例子中,在调用pushAll方法时生产了E 实例(produces E instances),在调用popAll方法时dst消费了E 实例(consumes E instances)。Naftalin与Wadler将PECS称为Get and Put Principle。

       java.util.Collections的copy方法(JDK1.7)完美地诠释了PECS:
[java]  view plain  copy
  1. public static <T> void copy(List<? super T> dest, List<? extends T> src) {  
  2.     int srcSize = src.size();  
  3.     if (srcSize > dest.size())  
  4.         throw new IndexOutOfBoundsException("Source does not fit in dest");  
  5.   
  6.     if (srcSize < COPY_THRESHOLD ||  
  7.         (src instanceof RandomAccess && dest instanceof RandomAccess)) {  
  8.         for (int i=0; i<srcSize; i++)  
  9.             dest.set(i, src.get(i));  
  10.     } else {  
  11.         ListIterator<? super T> di=dest.listIterator();  
  12.         ListIterator<? extends T> si=src.listIterator();  
  13.         for (int i=0; i<srcSize; i++) {  
  14.             di.next();  
  15.             di.set(si.next());  
  16.         }  
  17.     }  
  18. }  

PECS总结:

  • 要从泛型类取数据时,用extends;
  • 要往泛型类写数据时,用super;
  • 既要取又要写,就不用通配符(即extends与super都不用)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值