通用数组在直接引用时抛出ClassCastException(在通过helper方法调用时不会抛出)

时间:2013-09-11 16:36:15

标签: java arrays generics casting

这太神奇了!看看这个简单的代码:

public class ArrayOFMagic<T> {

    protected T[] array;

    protected int showMeYouRLength() {
        return array.length;
    }

    ArrayOFMagic() {
        array = (T[]) new Object[10];
    }

    protected void set(T value, int index) {
        array[index] = value;
    }

    public static void main(String[] args) {
        ArrayOFMagic<Integer> arrayOFMagic = new ArrayOFMagic<Integer>();
        System.out.println(arrayOFMagic.showMeYouRLength());
        System.out.println("MAGIC INCOMING");
        System.out.println(arrayOFMagic.array.length);
    }

}

输出:

  

10
MAGIC INCOMING
线程“主要”中的异常   java.lang.ClassCastException:[Ljava.lang.Object;无法施展   [Ljava.lang.Integer;在ArrayOFMagic.main(ArrayOFMagic.java:25)

我两次调用array.length。一次通过方法,直接一次。它在使用方法时继续,并在直接调用时抛出异常。 O.o有人解释?

编辑: 只是为了澄清:当没有直接调用时,类很有用。你可以在数组元素和所有..!

上设置setter / getter

4 个答案:

答案 0 :(得分:6)

更新答案:

免责声明:这个答案不是来自我,我问过一位前Sun员工,他在Java中使用泛型,为什么会这样。他的回答是:

第一个调用从泛型类本身内部访问该成员,该成员被删除到它的下界(在本例中为Object),因此在ref到array.length中没有强制转换。

但第二次调用是在泛型类型参数化实例上,因此类型变量(数组)绑定到Integer。

数组字段声明为T []类型,它绑定到Integer []。编译器发出的访问器代码将其强制转换为该类型,并且强制转换(在每个单词的意义上)。

OLD ANSWER: 这是实现课程的更好方法(作为zhong.j.yu答案的补充)。

import java.util.ArrayList;
import java.util.List;

public class NotSoMagical<T> {

    public List<T> arrayList;

    protected int length() {
        return arrayList.size();
    }

    NotSoMagical() {
        arrayList = new ArrayList<T>(10);
    }

    protected void set(T value, int index) {
        arrayList.set(index, value);
    }

    public static void main(String[] args) {
        NotSoMagical<Integer> notMagicalAtAll = new NotSoMagical<Integer>();
        System.out.println(notMagicalAtAll.length());
        System.out.println("MAGIC INCOMING");
        System.out.println(notMagicalAtAll.arrayList.size());
    }

}

答案 1 :(得分:4)

理想情况下,此行应在运行时立即失败

    array = (T[]) new Object[10];   // Object[] cannot be cast to Integer[]
遗憾的是,由于擦除,错误被吞噬了。错误可能会在以后弹出,但是何时何地?这是未定义的。语言规范没有说明对arrayOFMagic.array.length的访问是否成功。另见:Generics Oddity - I can insert a Long value into a Map<String, String> and it compiles and doesn't fail at runtime

建议:不要使用T[] array;只使用Object[] array

答案 2 :(得分:1)

使用泛型类型时,编译器会创建隐藏的强制转换,因此由于错误的数组创建而发生异常。但是您可以使用java.lang.reflect.Array更改构造函数来创建具有正确类型的数组:

ArrayOFMagic(Class<T> elementType) {
    array = (T[]) Array.newInstance(elementType, 10);
}

答案 3 :(得分:1)

在没有泛型的情况下写这个是最有启发性的。任何用泛型编写的程序都可以通过删除泛型和添加强制转换来转换为等效的程序。此转换称为类型擦除。在类型擦除之后,发生的事情变得非常明显:

public class ArrayOFMagic {

    protected Object[] array;

    protected int showMeYouRLength() {
        return array.length;
    }

    ArrayOFMagic() {
        array = new Object[10];
    }

    protected void set(Object value, int index) {
        array[index] = value;
    }

    public static void main(String[] args) {
        ArrayOFMagic arrayOFMagic = new ArrayOFMagic();
        System.out.println(arrayOFMagic.showMeYouRLength());
        System.out.println("MAGIC INCOMING");
        System.out.println(((Integer[])arrayOFMagic.array).length);
    }

}

(在这种情况下,您可能会认为转换为Integer[]是不必要的。智能编译器可以消除它,因为Object[]已经有名为length的字段。但是,作为一般规则,每当从泛型类中获取某些内容到泛型作用域的“外部”时,就会将其转换为适当的类型,并将type参数替换为。)