import java.util.ArrayList;public class GenericTest2 { @SuppressWarnings("unchecked")
public static void main(String[] args) { ArrayList<? extends Number> arr = new ArrayList<Integer>();
// ArrayList<? super Integer> arr0 = new ArrayList<Integer>();

// arr.add("5");//不能添加任何类型的对象,上界通配符能获得元素,不允许添加
// Number n = arr.get(0);
// System.out.println(n);
// Integer i = arr.get(0);


// arr0.add(4);//下届通配符能add元素,不允许获得
// arr0.add(4.1);
// Integer i = arr0.get(0); 不能get任何类型的对象

ArrayList<Double> arr1 = (ArrayList<Double>)arr;//能强转
arr1.add(3.14);

// ArrayList<Integer> arr2 = (ArrayList<Integer>)arr1;//不能强转
// ArrayList<Long> arr3 = (ArrayList<Long>)arr;//能强转
// ArrayList<Double> arr4 = (ArrayList<Double>)arr3;//不能强转
}}问题:
1、 ArrayList<? extends Number> arr = new ArrayList<Integer>();这条语句创建的ArrayList是什么类型的?2、 ArrayList<Double> arr1 = (ArrayList<Double>)arr;为什么能够强转?3、 ArrayList<Integer> arr2 = (ArrayList<Integer>)arr1;为什么不能强转?    我知道泛型是一种编译器语言,所以希望大大能够从编译器的角度帮我分析一下,再有就是泛型里面该死的擦除,到底是什么意思,如果能够帮忙解答,万分感谢!!

解决方案 »

  1.   

    鄙人认为:
    1、为ArrayList<Integer>类型;
    2、3参考://以下由d强制转换为i报错
    Double d=new Double(2.0);
    Integer i=new Integer(d);
    //以下有i2转换为d2通过
    Integer i2=new Integer(2);
    Double d2=new Double(i2);
    至于原因可以参考强制转换规则,另外这跟泛型关系不大,主要是强制转换的问题,什么编译器啊什么的更谈不上了。
    以上鄙人愚见,仅供参考。
      

  2.   

    LS说的差不多了,不过Integer i=new Integer(d);之所以出错是因为没有这样的构造函数,不是强行转换的出错,修改为 Integer i=new Integer((int)d);是可以的,这才是强行转换1、 ArrayList<? extends Number> arr = new ArrayList<Integer>();这条语句创建的ArrayList是什么类型的?
    创建的是ArrayList<? extends Number>类型,arr指向的是一个ArrayList<Integer>实例,即arr里存放的是Integer对象
    2、 ArrayList<Double> arr1 = (ArrayList<Double>)arr;为什么能够强转?
    因为Double也是Number的子类,而且double的精度比int高,转换时不存在精度自动放大
    比如 int a = 10; double b = a; 没问题,精度自动放大
    所以ArrayList<Double>可以全体自动转换3、 ArrayList<Integer> arr2 = (ArrayList<Integer>)arr1;为什么不能强转?
    因为int的精度比double小,所以double转为int时有精度损失,不能自动转换,必须强行转换
    所以ArrayList<Integer>不能全体转换,必须针对集合的元素一个一个强行转换
      

  3.   

    1、 如果为ArrayList<Integer>类型,那么为什么add(5);报错呢?
    2,3、 我没太明白您的意思,是不是说只要符合自动类型转换的才能强转,那如果将ArrayList<Double> arr1 = (ArrayList<Double>)arr;//能强转改成ArrayList<Short> arr1 = (ArrayList<Short>)arr;为什么却能够强转?再有麻烦您帮忙讲解一下关于泛型擦除行吗,我实在是没明白?
      

  4.   

    不好意思哈,我是刚刚学java,而且公认的比较笨,希望大家不要介意……
      

  5.   

    1、 如果为ArrayList<Integer>类型,那么为什么add(5);报错呢?
    因为arr是一个ArrayList<? extends Number>类型的引用,而它没有add(int)这个方法可以调用
    add(int)方法只有ArrayList<Integer>这个类型可以调用,也就是说add(int)方法不能用父类的引用去调用,只能用子类的引用调用2、
    ArrayList<Double> arr1 = (ArrayList<Double>)arr;//能强转
    改成
    ArrayList<Short> arr1 = (ArrayList<Short>)arr;为什么却能够强转?
    因为Short也是Number的子类,所以理由同Double,一样可以转换
      

  6.   

    1
    我上面说了,创建的是ArrayList<? extends Number>类型,但是arr指向的是一个ArrayList<Integer>实例,即arr里只能存放的Integer对象2
    我上面也说了,因为Double也是Number的子类,即<Double>符合<? extends Number>,而arr是ArrayList<? extends Number>类型,所以ArrayList<Double> arr1 = (ArrayList<Double>)arr;可以强行转换3
    受了1楼的一些干扰,arr1是ArrayList<Double>类型,而<Integer>不符合<Double>,
    所以ArrayList<Integer> arr2 = (ArrayList<Integer>)arr1;不能强行转换
    1、 如果为ArrayList<Integer>类型,那么为什么add(5);报错呢?
    arr不是ArrayList<Integer>类型,是ArrayList<? extends Number>类型,只是arr指向的是ArrayList<Integer>实例,ArrayList<E>的add(E e)方法里规定,add方法参数只能是和泛型一致的类型,即arr里的add方法参数是个? extends Number类型,而5是Integer,不是? extends Number类型,所以不能存放。可以通过ArrayList<Integer> arrx = (ArrayList<Integer>)arr; arrx.add(5)来达到目的。即先把? extends Number强行转换为Integer,然后再调用add方法,此时参数就是明确的Integer类型了。ArrayList<Double> arr1 = (ArrayList<Double>)arr;//能强转
    改成
    ArrayList<Short> arr1 = (ArrayList<Short>)arr;为什么却能够强转?
    这个问题和2一样,arr是ArrayList<? extends Number>类型,<Short>符合<? extends Number>,所以可以强行转换。
      

  7.   

    做了一下实验,arr虽然指向的是ArrayList<Integer>的实例,但是也可以存放其他? extends Number的类型,即
    ArrayList<? extends Number> arr = new ArrayList<Integer>();
    ArrayList<Integer> arr0 = (ArrayList<Integer>)arr;
    arr0.add(5); //存放Integer
    ArrayList<Double> arr1 = (ArrayList<Double>)arr;
    arr1.add(3.0); //存放Double如果是这样的,感觉
    ArrayList<? extends Number> arr = new ArrayList<Integer>();和
    ArrayList<? extends Number> arr = new ArrayList<Number>();是等效的,即后面的实现类型是什么都没关系,好像以<? extends Number>优先。这个也没仔细研究过,有时间再看看。
      

  8.   

    第一个问题:ArrayList<? extends Number>声明集合里放入的对象只能是extends Number的,也就是你知道集合里面的东西”至少“是一个Number,至于他是int还是double你知道不?不可能知道,所以你不能插入(add),因为这是不安全的
      

  9.   

    泛型擦除的意思就是ArrayList<Integer>,编译器在运行前,也就是编译器会检查你的类型,必须满足要求,但是在方法内部,所有的泛型都是被当做Object的,也就是,虚拟机丢失了泛型的类型(本来你是知道类型是Integer的,但是被当做Object了),这么做的原因是java的设计者在一开始并没有把泛型加入计划内,后来为了兼容以前的老代码,不得以采用擦除的方式来对待泛型
      

  10.   

    简单说下,楼主既然知道泛型是编译器行为,也就是说与JVM没有关系;那么所说的类型变量的擦除就是发生在由.java文件到.class文件的编译过程中,如ArrayList<T>类型变量擦除后就是ArrayList,这个ArrayList就是ArrayList<T>的原型,ArrayList<T>叫做泛型;Java是强类型语言,T 擦除后总得有个类型,T 如果没有限定,就用Object替换,有的话就是extends后的第一个类型替换; 既然擦除了,但JVM还是要知道确切的类型,要转换到原来的类型,在没有泛型时我们是强制类型转换的,所以编译器就帮我们做了这么一步,这样做是不是类型转换更安全性了呢。编译器为什么要擦除,简单理解就是JVM不识别,编译器把原来程序员做的类型转变现在变成了编译器来处理。
      

  11.   

    下面说下其他的问题:
    ArrayList<? extends Number> arr = new ArrayList<Integer>();
    这里创建的是ArrayList<Integer>类型,它是ArrayList<? extends Number>的子类型;
    arr里面元素的实际类型是Integer. 但你这里使用了个 ? 通配符,表示Number的任何子类型,arr无法增加任何具体类型元素,即add(具体类型);只能arr.add(null); 原因就是这里类型变量<? extends Number>告诉编译器保存的元素类型是Number的任何子类型,编译器无法确认。(是不是很别扭的)ArrayList<Double> arr1 = (ArrayList<Double>)arr;
    这里其实是转换为子类型,ArrayList<Double>是ArrayList<? extends Number>的子类型,当然能过强制转换。ArrayList<Integer> arr2 = (ArrayList<Integer>)arr1;
    这里主要是ArrayList<Integer>和ArrayList<Double>没有任何关系,虽然Integer和Double同是Number的子类型,但ArrayList<Integer>)或ArrayList<Double>并不是ArrayList<Number>的子类型,所以明显ArrayList<Double>不能强制类型转换为ArrayList<Integer>。