为什么不创建一个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 publicT [] toArray(T [] a){
325 if (a.length326 //创建一个运行时类型的新数组,但我的内容:
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[]
并在构造实例时定义E
是String
。 它是一个Object[]
。 这是因为Java不知道E
在运行时是什么类型,因为在这个例子中,我们没有为E
定义父类。 因此,它将自动将Object
作为其父级。
public class ArrTest
, public class ArrTest
与public class ArrTest
。
Java不知道E
在运行时是什么,因为它unchecked
。 Unchecked
意味着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
的实例。 如果创建一个Integer
的ExampleType
,则需要使用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
始终返回预期结果。