将返回T[](其中T为Byte[])转换为Byte[]

3
我有一个Java JUnit 4测试和一个创建通用数组的通用类。当我创建一个返回此通用数组的方法时,返回它时会出现错误消息。如果我将T实例化为Byte并将其返回到ArrayList,则可以正常工作。

为什么数组似乎已被实例化并且可用(在更复杂的类上进行了测试),但我无法返回对该数组的引用?

错误:

java.lang.ClassCastException: [Lcom.waikato.assignment.Object; cannot be cast to [Ljava.lang.Byte;
at com.waikato.testing.TestJava.test(TestJava.java:20)

类:

public class TestClass<T> {
    T[] array;

    @SuppressWarnings("unchecked")
    public TestClass() {
        array = (T[]) new Object[32];
    }

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

    public boolean doThing() {
        T[] t = array;

        return t == array && t != null;
    }
}

测试:

public class TestJava {

    @Test
    public void test() {
        TestClass<Byte> t = new TestClass<Byte>();
        Byte[] b = t.getArray(); // Crash caused

        assertTrue(t.doThing()); // Works when above line is removed.
        assertTrue(b.length != 0);
    }

}
4个回答

5
通过反射可以构造一个“通用”的数组:
T[] array = (T[]) Array.newInstance(Byte.class, 32)

Byte.class替换为对所需类的引用。换句话说:

public class TestClass<T> {
    T[] array;

    @SuppressWarnings("unchecked")
    public TestClass(Class<T> type) {
        array = (T[]) Array.newInstance(type, 32);
    }

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

    public boolean doThing() {
        T[] t = array;

        return t == array && t != null;
    }
}

您可以按如下方式进行验证:
public static void main(String[] args) {
    TestClass<Byte> test = new TestClass<Byte>(Byte.class);
    // No ClassCastException here
    Byte[] array = test.getArray();
    System.out.println(Arrays.asList(array));
}

由于类型擦除,您无法解决此问题,除非使用Class<?>对象作为构造函数参数。


但是如果修复类,您不会破坏方法的通用性吗?这样,对于每种类型,您都将拥有“ClassCastException”,但是Byte除外。 - Boris Strandjev
TestClass 中没有通用的方法。 TestClass 本身是通用的... - Lukas Eder
这正朝着我想要的方向发展(编辑后的问题,希望更好地反映这一点)。我需要一个通用数组,并遇到了新的Object[x]方法和Array.newInstance()方法。令人失望的是,没有办法在不传递Class<T>对象的情况下使用Array.newInstance()方法。 - Simon Campbell
@SimonCampbell:没有。Java数组使用协变性。如果你想要一个T[]数组,你需要以Class<T>的形式提供T的类型。由于<T>本身被擦除,泛型信息在运行时不再可用。Boris提到了一个技巧,它在一些特殊情况下是有效的。但在这种情况下,它不起作用... - Lukas Eder

2
array = (T[]) new TestClass[32];

我觉得这不像一个Byte[],编译器应该会警告你在运行时忽略了泛型转换。

这是通用的解决方案。

TestClass(Class<T> tclass){
   //create an array for the generic type
   array = (T[])java.lang.reflect.Array.newInstance(tclass,32);
}


TestClass<Byte> bTest = new TestClass(Byte.class);

1

有一种方法可以找到泛型类的实际类型参数。

我只是将主类的构造函数更改为以下内容。

public class TestClass<T> {
T[] array;

@SuppressWarnings("unchecked")
public TestClass() {
    Class<T> objClass;
    objClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; // Found the type paramenter
    array = (T[]) Array.newInstance( objClass, 32);
}

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

public boolean doThing() {
    T[] t = array;

    return t == array && t != null;
} }

还有测试方法...

@Test
public void test() {
    TestClass<Byte> t = new TestClass<Byte>(){}; // Added {}
    Byte[] b = t.getArray(); 

    Assert.assertTrue(t.doThing()); 
    Assert.assertTrue(b.length != 0);
}

我刚刚避免了将类类型参数传递给构造函数。 - vinodn
1
有趣...但是,你需要每个<T>类型一个子类,甚至对于匿名子类可能需要每个实例...而且你必须永远不要忘记继承TestClass...也许将TestClass定义为abstract - Lukas Eder
没错,将它定义为抽象类可以在编译时避免这个问题。 但这取决于类的作者和他的需求,是将其定义为抽象类还是传递一个类类型。 - vinodn

0

这行代码有误:

public TestClass() {
    array = (T[]) new TestClass[32];
}

这与您的问题标题“T为Byte时”的描述相矛盾,因为T已经被初始化为TestClass。

一个想法是将数组更改为List<T>。


在Java中,new T[]不起作用,因为泛型类型在运行时不存在。 - josefx
@josefx 是的,非常抱歉。正在更新答案。 - maksimov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接