为什么不创建一个Object 并转换为generics类型? 解决方案是什么?

一些开发人员通过创建Object[]并转换为generics类型来创建generics类型的数组,如以下示例代码所示:

 public class ArrTest { public void test(E a){ E[] b = (E[])new Object[1]; b[0] = a; System.out.println(b[0]); } public static void main(String[] args){ ArrTest t = new ArrTest(); t.test("Hello World"); } } 

该示例将起作用并且只有一个警告: Type safety: Unchecked cast from Object[] to E[]

气馁了吗? 这是创建generics类型数组的最佳方法吗? 如果我在我的软件中广泛使用此对象,是否会导致意外结果或exception?

您收到的警告只是指出编译器无法根据Java语言规范指定的规则确保静态类型安全。 换句话说,它指出静态类型安全已经被破坏。

然而,这并没有使这个成语明确地气馁 。 这是来自JDK本身的完全合法的案例(以Grepcode格式):

  323 @ SuppressWarnings (“未选中”) 
324 public T [] toArray(T [] a){
325 if (a.length 326 //创建一个运行时类型的新数组,但我的内容:
327 return (T [])数组。 copyOf (elementData,size, a。getClass ());
328系统。 arraycopy (elementData,0,a,0,size);
329 if (a.length> size)
330 a [size] = null ;
331 返回 ;
332 }

尽管使用的向下转换未经检查,但更高级别的逻辑清楚地表明它是类型安全的。

在问题的例子中, b变量不是String[]即使我们将它转​​换为E[]并在构造实例时定义EString 。 它是一个Object[] 。 这是因为Java不知道E在运行时是什么类型,因为在这个例子中,我们没有为E定义父类。 因此,它将自动将Object作为其父级。

public class ArrTestpublic class ArrTestpublic class ArrTest

Java不知道E在运行时是什么,因为它uncheckedUnchecked意味着Java不会检查E类型是否是已定义父类的扩展或实现。 因此,Java在运行时唯一知道的是

因此

E[] b = (E[]) new Object[1];

将执行为

Object[] b = (Object[]) new Object[1];

这就是为什么该示例不会抛出ClassCastException并且会使开发人员感到困惑的原因。

如果我们尝试将b用作真正的String[]则Java将抛出ClassCastException因为Java将其视为Object[] 。 例如,如果我们将方法更改为:

 public E[] test(E a){ E[] b = (E[])new Object[1]; b[0] = a; System.out.println(b[0]); return b; } public static void main(String[] args){ ArrTest t = new ArrTest(); String[] result = t.test("Hello World"); } 

现在我们将在String[] result收到ClassCastException ,因为返回的类型将是Object[] ,我们试图将它存储在String[]变量中。 Java将看到类型差异并抛出exception。

这就是为什么不鼓励将Object[]强制转换为通用数组,这只会导致混淆。

在写这个答案之前,我创建了一个测试用例,其中有一些可能的方法来创建一个通用数组,我得出结论,这是最好的方法:

 public class ExampleType{ public  T[] bestMethod(T[] array) { if(array.length < testSize) array = (T[]) Array.newInstance(array.getClass().getComponentType(), testSize); //Type safety: Unchecked cast from Object to T[] System.out.println("in this case: "+array.getClass().getComponentType().getSimpleName()); return array; } } 

它保证返回与作为参数传递的数组相同类型的数组,并且它必须是ExampleType定义的A的实例。 如果创建一个IntegerExampleType ,则需要使用Integer[]作为参数。 如果你不想特别想要一个Integer数组,但你想存储任何类型的数字,你可以使用Number[]作为参数。

如果您不需要类中的generics类型,则可以将其简化为:

 public  T[] bestMethod(T[] array) 

如果您希望它仅返回Number子类:

 public  T[] bestMethod(T[] array) 

如果您想自己测试一下,这是我的测试用例:

 public class Test { public static class ArrTest { public void test(E a){ E[] b = (E[])new Object[1]; b[0] = a; System.out.println(b[0]); } public E[] test2(E a){ E[] b = (E[])new Object[1]; b[0] = a; System.out.println(b[0]+" "+b.getClass().getComponentType()); return b; } public static void main(String[] args){ ArrTest t = new ArrTest(); t.test("Hello World"); try{String[] result = t.test2("Hello World");}catch(Exception e){System.out.println(e);} } } public static void main(String[] args) { ArrTest.main(args); System.out.println("#############\nWe want an array that stores only integers, sampledata: 1, samplearray: Integer"); test(new ExampleType(Integer.class), 1, new Integer[0], new Integer[10]); System.out.println("#############\nWe want an array that stores any type of Number, sampledata: 2L, samplearray: Number"); test(new ExampleType(Number.class), 2L, new Number[0], new Number[10]); System.out.println("#############\nWe want an array that stores any type of CustomNumberA, sampledata: CustomB(3L), samplearray: CustomNumberA"); test(new ExampleType(CustomNumberA.class), new CustomNumberB(3L), new CustomNumberA[0], new CustomNumberA[10]); System.out.println("#############\nWe want A to be any type of number but we want to create an array of CustomNumberA, sampledata: CustomB(3L), samplearray: CustomNumberA"); test(new ExampleType(Number.class), new CustomNumberB(3L), new CustomNumberA[0], new CustomNumberA[10]); } public static  void test(ExampleType testType, A sampleData, A[] smallSampleArray, A[] bigSampleArray) { Class clazz = testType.clazz; System.out.println("#############\nStarting tests with ExampleType<"+clazz.getSimpleName()+">"); System.out.println("============\nCreating with badMethod()..."); A[] array; try { array = testType.badMethod(); testType.executeTests(array); } catch(Exception e){ System.out.println(">> ERR: "+e); } System.out.println("============\nCreating with alsoBadMethod("+sampleData+" ["+sampleData.getClass().getSimpleName()+"])..."); try { array = testType.alsoBadMethod(sampleData); testType.executeTests(array); } catch(Exception e){ System.out.println(">> ERR: "+e); } System.out.println("============\nCreating with nearlyGoodMethod("+smallSampleArray.getClass().getSimpleName()+" len: "+smallSampleArray.length+")..."); try { array = testType.nearlyGoodMethod(smallSampleArray); testType.executeTests(array); } catch(Exception e){ System.out.println(">> ERR: "+e); } System.out.println("============\nCreating with nearlyGoodMethod("+bigSampleArray.getClass().getSimpleName()+" len: "+bigSampleArray.length+")..."); try { array = testType.nearlyGoodMethod(bigSampleArray); testType.executeTests(array); } catch(Exception e){ System.out.println(">> ERR: "+e); } System.out.println("============\nCreating with bestMethod("+smallSampleArray.getClass().getSimpleName()+" len: "+smallSampleArray.length+")..."); try { array = testType.bestMethod(smallSampleArray); testType.executeTests(array); } catch(Exception e){ System.out.println(">> ERR: "+e); } System.out.println("============\nCreating with bestMethod("+bigSampleArray.getClass().getSimpleName()+" len: "+bigSampleArray.length+")..."); try { array = testType.bestMethod(bigSampleArray); testType.executeTests(array); } catch(Exception e){ System.out.println(">> ERR: "+e); } } @RequiredArgsConstructor @ToString() public static class CustomNumberA extends Number{ @Delegate final Long n; } public static class CustomNumberB extends CustomNumberA{ public CustomNumberB(Long n) { super(n); } } @RequiredArgsConstructor public static class ExampleType{ private int testSize = 7; final Class clazz; public A[] badMethod() { System.out.println("This will throw a ClassCastException when trying to return the array because Object is not a type of "+clazz.getSimpleName()); A[] array = (A[]) new Object[testSize]; //Warning: Type safety: Unchecked cast from Object[] to A[] System.out.println("Array of "+array.getClass().getComponentType()+" created"); return array; } public A[] alsoBadMethod(A sampleType) { System.out.println("Will not respect A type ("+clazz.getSimpleName()+"), will always use the highest type in sampleType and tell that it's A[] but it's not, in this case will return "+sampleType.getClass().getSimpleName()+"[] and said it was "+clazz.getSimpleName()+"[] while developing"); A[] array = (A[]) Array.newInstance(sampleType.getClass(), testSize); //Type safety: Unchecked cast from Object to A[] return array; } public A[] nearlyGoodMethod(A[] array) { System.out.println("The only guarantee is that the returned array will be of something that extends A ("+clazz.getSimpleName()+") so the returned type is not clear, may be of A or of the type passed in the argument but will tell it's A[] but may not be"); if(array.length < testSize) array = (A[]) Array.newInstance(array.getClass().getComponentType(), testSize); //Type safety: Unchecked cast from Object to A[] System.out.println("in this case: "+array.getClass().getComponentType().getSimpleName()+"[], expecting: "+clazz.getSimpleName()+"[]"); return array; } public  T[] bestMethod(T[] array) { System.out.println("It's guaranteed to return on array of the same type as the sample array and it must be an instance of A, so, this is the best method"); if(array.length < testSize) array = (T[]) Array.newInstance(array.getClass().getComponentType(), testSize); //Type safety: Unchecked cast from Object to T[] System.out.println("in this case: "+array.getClass().getComponentType().getSimpleName()+"[], expecting: "+array.getClass().getComponentType().getSimpleName()+"[]"); return array; } public void executeTests(A[] array) { tryToSet(array, 0, 1); tryToSet(array, 1, 2L); tryToSet(array, 2, 3.1); tryToSet(array, 3, 4F); tryToSet(array, 4, (byte)0x5); tryToSet(array, 5, new CustomNumberA(6L)); tryToSet(array, 6, new CustomNumberB(7L)); } public void tryToSet(A[] array, int index, Object value) { System.out.println("Trying to set "+value+" ("+value.getClass().getSimpleName()+") at "+index+" in a array of "+array.getClass().getComponentType().getSimpleName()); try { if(array instanceof Object[]) ((Object[]) array)[index] = value; else array[index] = (A) value; //Type safety: Unchecked cast from Object to A System.out.println("## OK: Success: "+array.getClass().getComponentType().getSimpleName()+"["+index+"] = "+array[index]); } catch(Exception e){ System.out.println(">> ERR: "+e); } } } } 

以下是测试结果......您可以看到bestMethod始终返回预期结果。

http://pastebin.com/CxBSHaYm