T类数组(Java泛型)

时间:2015-06-11 10:53:15

标签: java

虽然此代码成功编译,但它会在运行时抛出类强制转换异常错误:

class Test {
    public static void main(String[] args) {
        MyArray<String> x = new MyArray<>();
        x.a[0] = "test string";
    }
}

class MyArray<T> {
    T[] a;

    MyArray() {
        this.a = (T[]) new Object[1];
    }
}

错误消息:

Exception in thread "main" java.lang.ClassCastException:
[Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
    at Test.main(Test.java:4)

我所知道的一个可能但简单的解决方案是将数组声明为 Object 类型的数组,然后在想要从数组中检索数组时将数组元素转换回T ,像这样:

class Test {
    public static void main(String[] args) {
        MyArray<String> x = new MyArray<>();
        x.a[0] = "test string";
        System.out.println(x.get(0));
    }
}

class MyArray<T> {
    Object[] a;

    MyArray() {
        this.a = new Object[1];
    }

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

这很好用。但是这种方法的问题在于我现在可以把任何东西放在数组中然后保持安全和声音,直到我试图将它从数组中拉出来,这将给我一个大的,胖的类转换异常错误!

    MyArray<String> x = new MyArray<>();
    x.a[0] = 34;
    System.out.println(x.get(0)); // run-time error

如果我们运行这段代码,这就是我们得到的:

 Exception in thread "main" java.lang.ClassCastException: 
 java.lang.Integer cannot be cast to java.lang.String
    at Test.main(Test.java:5)

显然,当所有数组元素属于同一类型而不是 Object 类型的通用对象时,它会好得多。必须有办法做到这一点。

顺便说一下,我在这里展示的示例代码已经大大简化了。我知道我可以使用set和get方法来进行必要的类型转换,当然,这将有助于使元素完全安全地添加和获取元素进出数组。这个问题是,如果我在类中有很多其他方法也进行大量的转换,那么编写所有这些转换操作符的单调乏味会变得非常痛苦。我认为如果我可以将单词中的所有元素作为特定类型存储在单词go中,那将是非常好的。这将简化编码过程并使代码更清晰,更易于阅读。所以,任何你想要通过我的方式的智慧都是最受欢迎的,并且会非常感激。

3 个答案:

答案 0 :(得分:5)

  

但这种方法的问题在于我可以在数组中放置任何东西

是。这就是你不公开数组的原因:

class MyArray<T> {
    private Object[] a;

    MyArray() {
        this.a = new Object[1];
    }

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

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

set()会阻止你做错事。

由于大小为1的数组不是很有用,我在这里修改一下:

class MyArray<T> {
    private Object[] a;

    MyArray(int size) {
        this.a = new Object[size];
    }

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

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

答案 1 :(得分:0)

ClassCastException由于 type erasure 而发生。基本上它归结为泛型是完全在编译器中发生的语法糖。由于T可以是任何数组的运行时类型必须是Object,因为它可以存储任何内容。

解决此问题的一种方法是将类传递给构造函数,以便它可以使用它来创建具有正确运行时类型的数组:

import java.lang.reflect.Array;

class MyArray<T> {
    T[] a;

    MyArray(Class<T> type) {
        this.a = (T[]) Array.newInstance(type, 1);
    }

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

你会这样使用它:

MyArray<String> myStringArray = new MyArray<>(String.class);

不理想,但我认为解决问题的方法最不优雅。

答案 2 :(得分:0)

您可以使用Object数组并将其设为私有。你需要制作一套&#39;方法也是如此,你不能放任何东西。这就是ArrayList的工作方式。

其他解决方案是使用反射Array类创建T数组。为此,您必须将实际类型类作为参数传递:

class Test {
    public static void main(String[] args) {
        MyArray<String> x = new MyArray<>(String.class);
        x.a[0] = "test string"; // works
        x.a[1] = 1; // does not even compile
    }
}

class MyArray<T> {
    T[] a;

    MyArray(Class<T> clazz) {
        this.a = (T[]) Array.newInstance(clazz, 2);
    }
}