10

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

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
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; 不能转换为 [Ljava.lang.Integer; 在 ArrayOFMagic.main(ArrayOFMagic.java:25)

我两次调用array.length。一次通过方法和一次直接。使用方法时继续,直接调用时抛出异常。哦有人解释一下吗?

编辑:澄清一下:类在不直接调用时效果很好。您可以在数组元素和所有..!

4

4 回答 4

6

更新答案:

免责声明:这个答案不是来自我,我问过一位在 Java 中从事泛型工作的前 Sun 员工,为什么会发生这种情况。他的回答:

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

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

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

旧答案: 这是实现课程的更好方法(作为 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());
    }

}
于 2013-09-11T16:57:28.050 回答
4

理想情况下,这条线应该在运行时立即失败

    array = (T[]) new Object[10];   // Object[] cannot be cast to Integer[]

不幸的是,由于擦除,错误被吞没了。该错误可能会在以后弹出,但是何时何地?那是未定义的。语言规范没有说明访问是否arrayOFMagic.array.length应该成功。另请参阅:Generic Oddity - 我可以将 Long 值插入 Map<String, String> 并且它可以编译并且在运行时不会失败

建议:不要使用T[] array;简单地使用Object[] array

于 2013-09-11T16:45:42.360 回答
1

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

ArrayOFMagic(Class<T> elementType) {
    array = (T[]) Array.newInstance(elementType, 10);
}
于 2013-09-11T17:59:35.040 回答
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。但是,作为一般规则,每当您从泛型类中获取某些内容时在泛型范围内,它被转换为适当的类型,并替换了类型参数。)

于 2013-09-11T21:53:03.047 回答