2 qq 22246675 qq_22246675 于 2017.09.12 17:52 提问

JAVA泛型的问题:急求大神。一个关于"泛型"和Object[]数组的坑

下面是我写的一个Java代码,
图片说明
我知道24行的直接转换是不行的,因为内部数组本质是一个Object[]的数组,但是运行该程序是会报错的,报错的位置是在25行,错误信息是类型转换错误。但是26行这样的写法是没有问题的,如果将25行注释掉,程序可以正常运行。我想知道的是25和26行代码区别在哪里,为什么25行是会报错的,是编译器做了什么事情吗?我看过这个源代码编译后的class文件,25行加入了一个类型检查,但是为什么会加上类型检查?挺急的,跪求大神解答。

源代码如下:
public class GenericArray {

private Object[] array;

public GenericArray(int size) {
    array = new Object[size];
}

public void put(int index, T item) {
    array[index] = item;
}

public T get(int index) {
    return (T) array[index];
}

public T[] rep() {
    return (T[]) array;
}

public static void main(String[] args) {
    GenericArray<Integer> ga = new GenericArray<>(10);
    for(int i = 0; i < 10; i++) {
        ga.put(i, i);
    }
    for(int i = 0; i < 10; i++) {
        System.out.println(ga.get(i) + " ");
    }
    System.out.println();
    try {
        System.out.println(ga.rep().toString());
        System.out.println(((Object)ga.rep()).toString());
    } catch(Exception e) {
        e.printStackTrace();
    }
}

}

8个回答

tomorrow_fine
tomorrow_fine   2017.09.12 20:37

我们无法获知类型T是否含有方法toString()方法。这是为什么呢?就是因为万恶的擦除引起的,在Java代码运行的时候,它会将泛型类的类型信息T擦除掉,就是说运行阶段,泛型类代码内部完全不知道类型参数的任何信息,即使toString()是object的方法。

qq_22246675
qq_22246675 回复无名同学: 我的意思是为啥哪个地方会有强制类型转换的过程
2 个月之前 回复
tomorrow_fine
tomorrow_fine 回复菩提本无树明镜亦非台: Object object = 1; System.out.println((Integer) object); Object[] objects = new Object[]{1, 2, 3}; System.out.println((Integer[]) objects);类型擦除后的强转,数组会转化失败
2 个月之前 回复
qq_22246675
qq_22246675 是Object的方法又如何 我又不是打印的信息有误 是强转异常了 不是你说的这个事啊 大兄弟
2 个月之前 回复
c15158032319
c15158032319   2017.09.12 22:24
    @Test
    public void test(){
        Integer[] ii = new Integer[]{};
        Object[] oo = new Object[]{};
        ii = (Integer[])oo;
    }

事实是因为你的array真实类型是Object[],所以此处强转为Integer[]会报错。除非arry = new Integer[]{},那么这里转换就不会报错了。

luaifei2008
luaifei2008 回复外星喵: 在Java中数组是具有协变性的,Integer[]是Object[]的子类,Java认为没毛病~
2 个月之前 回复
qq_22246675
qq_22246675 为啥那个位置是Integer[] 你不能因为报个错就确定那是Integer[]数组吧 我知道Object[] 肯定不能转为Integer[] 问题是哪个25行的位置发生了什么 它为什么进行强转
2 个月之前 回复
c15158032319
c15158032319 父类强转成子类并没有问题,就像Object可以强转成Integer,但是问题是Interger[]是Object[]的子类么?
2 个月之前 回复
fanyizhuang
fanyizhuang   2017.09.12 17:59

用String.valueOf(ga2.rep())看下

qq_22246675
qq_22246675 试了 返回的肯定是一个Object[]数据类型
2 个月之前 回复
Small_Mouse0
Small_Mouse0   Ds   Rxr 2017.09.12 18:02

报的什么错,,,感觉不应该呀,,,题主能把代码发一下吗?

qq_22246675
qq_22246675 回复小鼠标丶: 你用vim或者普通的txt写一个java源文件自己去编译 不可能哪个TXT的编辑器会提示你返回的是Integer[]吧 现在的问题是它为什么会这么做 而是那个报错的地方到底发生了什么? 是什么导致的它这么做的
2 个月之前 回复
qq_22246675
qq_22246675 回复小鼠标丶: 我想知道的是为啥那是Integer[] 我知道IDE提示的Integer[] 如果你要用VIM编辑java源文件 肯定不会这么提示你
2 个月之前 回复
Small_Mouse0
Small_Mouse0 回复菩提本无树明镜亦非台: public T[] rep() { return (T[]) array; },,,IDE中可能提示的是Intager[],,其实返回的是Object[],,直接当Intager[]使用就会报错,原因是,,Object[]无法强转为Intager[],,但是Object[],,,可以强转为Object使用,,打印toString时就会显示类型是Object类型,,,,如果不强转就会当Intager使用,,就会报错。,,,题主可能有这样的问题,为什么在强转为T[],时没报错,,这个可能是规定吧,,不知道那位大神能解释,这个转换为什么不报错。
2 个月之前 回复
qq_22246675
qq_22246675 我把代码发上去了
2 个月之前 回复
phenomenonsTell
phenomenonsTell   2017.09.12 19:22

这样就不会报错了,你的那个rep()方法想要返回数组的话直接调用Arrays工具类的方法就行。
我的eclipse的输出结果:
0 1 2 3 4 5 6 7 8 9
GenericArray [array=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]]

 import java.util.Arrays;

public class GenericArray<T> {
    private T[] array;

    public GenericArray(int size) {
        array = (T[]) new Object[size];
    }

    public void put(int index, T item) {
        array[index] = item;
    }

    public T get(int index) {
        return array[index];
    }

    @Override
    public String toString() {
        return "GenericArray [array=" + Arrays.toString(array) + "]";
    }

    public static void main(String[] args) {
        GenericArray<Integer> ga = new GenericArray<>(10);
        for(int i = 0; i < 10; i++) {
            ga.put(i, i);
        }
        for(int i = 0; i < 10; i++) {
            System.out.print(ga.get(i) + " ");
        }
        System.out.println();
        try {
            System.out.println(ga.toString());
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
}
qq_22246675
qq_22246675 我只是想知道为啥25行会运行时报错 你这答非所问啊 大兄弟 其他的情况不需要考虑 我只想知道那个位置到底发生了什么
2 个月之前 回复
weixin_40214842
weixin_40214842   2017.09.12 21:21

发来看一下帮你试一下

luaifei2008
luaifei2008   2017.09.12 22:57

首先,子类数组转父类数组没问题,如 String[] s; 可以转为 Object[] o = (Object[])s;
但父类数组不能转成子类数组,因为不能保证父类的元素都符合子类类型,因为同一个父类可以有不同的子类

所以
第一种情况,Object[] o是确定的类型,String[] s也是确定的类型,所以编译期间编译器能知道父类数组不能转成子类数组,所以报错

而第二种是泛型擦除
Object[] o是确定的类型,T[] t是泛型,编译的时候编译期不知道T[]是什么类型,而把泛型擦除,即把T[]变为Object[](Object[]是父类数组没问题),所以编译器只是给出警告

参考链接:http://bbs.csdn.net/topics/390028191

qq_22246675
qq_22246675 回复luaifei2008: 好吧 大兄弟 我一直在考虑它为什么会发生强转 编译器到底做了什么 我也没搞懂。。。
2 个月之前 回复
luaifei2008
luaifei2008 回复菩提本无树明镜亦非台: 由于泛型擦除的原因,rep()方法实际返回的是Object[]数组,并非Integer[]数组,从报错可以看到是Object[]转Integer[]失败。至于为什么会存在类型转换这一步,目前还没有考虑清楚。
2 个月之前 回复
qq_22246675
qq_22246675 我只是想知道为啥25行会运行时报错 你这答非所问啊 大兄弟
2 个月之前 回复
luaifei2008
luaifei2008 也就是说,泛型的类型转换T[] array,不管泛型为何种类型,最后都会变成Object[] array,Object[]是父类数组肯定是没问题的,所以编译器只会警告提醒。
2 个月之前 回复
u011857460
u011857460   2017.09.13 09:27

3行定义,改成T[] array

qq_22246675
qq_22246675 那个没关系的 因为泛型擦除之后 都是Object[] 你那改一下只是换个位置而已 没解决问题
2 个月之前 回复
Csdn user default icon
上传中...
上传图片
插入图片