为什么不创建一个Object []并转换为泛型?什么是解决方案? [英] Why not create an Object[] and cast to a generic type? What's the solution?
问题描述
有些开发人员通过创建 Object []
并转换为泛型类型来创建泛型类型的数组,如下例所示:
public class ArrTest< E> {
。
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< String> t = new ArrTest< String>();
t.test(Hello World);
$ p这个例子可以工作,只是有一个警告: code>类型安全性:未检查从Object []转换为E []
不鼓励?这是创建一个泛型类型数组的最佳方法吗?如果我在我的软件中广泛使用这个对象,这会导致意想不到的结果或异常吗?在解决问题的例子中,<$ c即使我们将它转换为
E [] $ c>,$ c> b
变量不是String []
$ c>,并在构造实例时定义E 为 String
。它是一个Object []
。发生这种情况是因为Java在运行时不知道E
是什么类型,因为在本例中,我们没有为E
。因此,它会自动将Object
作为其父项。
换句话说,
public class ArrTest< E>
与public class ArrTest< E extends Object>
。
<由于未经检查,Java并不知道 E
是在运行时。未检查
意味着Java不会检查E
类型是否是定义父类的扩展或实现。所以,Java在运行时唯一知道E
的是< E extends Object>
。
$ b $ p
E [] b =(E [])new Object [1];
将作为
对象[] 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< String> t = new ArrTest< String>();
String [] result = t.test(Hello World);
$ / code>现在我们将收到一个
ClassCastException $ c因为返回的类型是
Object []
,所以我们试图存储它,因为$ c>在String [] result
在String []
变量中。 Java会看到类型差异并抛出异常。
这就是为什么将
Object []
转换为泛型数组的原因不鼓励,它只会导致混淆。
在写这个答案之前,我创建了一个测试用例,用一些可能的方法来创建一个通用数组,并且我得出结论:这是最好的方法:
public class ExampleType< A extends Number> {
only:
public< T extends A> T [] bestMethod(T [] array)
{
if(array.length< testSize)
array =(T [])Array.newInstance(array.getClass()。getComponentType (),testSize); //类型安全性:未检查从Object转换为T []
System.out.println(在这种情况下:+ array.getClass()。getComponentType()。getSimpleName());
返回数组;
$ b它保证返回一个相同类型的数组该数组作为参数传递,它必须是
ExampleType< A extends Number>
中定义的A
的实例。如果创建整数
的ExampleType
,则需要使用Integer []
作为参数。如果你不需要一个
Integer
的数组,但是你想存储任何类型的数字,你可以使用Number [] $ c
$ b 如果你不需要类中的泛型类型,你可以将它简化为:
public< T> T [] bestMethod(T [] array)
如果您希望它返回<$的子类c $ c> Number
public< T extends Number> T [] bestMethod(T [] array)
这是我的测试用例,如果你想测试它您自己:
public class Test {
public static class ArrTest< E>
{
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< String> t = new ArrTest< String>();
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(############# \\\
我们想要一个只存储整数的数组,samplesata:1,samplearray:Integer);
test(new ExampleType< Integer>(Integer.class),1,new Integer [0],new Integer [10]);
System.out.println(############# \\\
我们想要一个数组来存储任何类型的数字,sampledata:2L,samplearray:Number) ;
test(新的ExampleType< Number>(Number.class),2L,new Number [0],new Number [10]);
System.out.println(############# \\\
我们需要一个存储任何类型的CustomNumberA的数组,samplesata:CustomB(3L),samplearray: CustomNumberA);
test(new ExampleType< CustomNumberA>(CustomNumberA.class),new CustomNumberB(3L),new CustomNumberA [0],new CustomNumberA [10]);
System.out.println(############# \我们希望A是任何类型的数字,但是我们想创建一个CustomNumberA数组, :CustomB(3L),samplearray:CustomNumberA);
test(new ExampleType< Number>(Number.class),new CustomNumberB(3L),new CustomNumberA [0],new CustomNumberA [10]);
}
public static< A extends Number> void test(ExampleType< A> testType,sampleData,A [] smallSampleArray,A [] bigSampleArray)
{
Class< A> clazz = testType.clazz;
System.out.println(############# \\\
使用ExampleType开始测试<+ clazz.getSimpleName()+>);
System.out.println(============ \\\
Creating with badMethod()...);
A []数组;
尝试
{
array = testType.badMethod();
testType.executeTests(array);
}
catch(Exception e){System.out.println(>> ERR:+ e); }
System.out.println(============ \\\
使用alsoBadMethod(+ sampleData +[+ sampleData.getClass()。getSimpleName()+])创建...);
尝试
{
array = testType.alsoBadMethod(sampleData);
testType.executeTests(array);
}
catch(Exception e){System.out.println(>> ERR:+ e); }
System.out.println(============ \\\
使用almostGoodMethod(+ smallSampleArray.getClass()。getSimpleName()+len:+ smallSampleArray.length + )......);
尝试
{
array = testType.nearlyGoodMethod(smallSampleArray);
testType.executeTests(array);
}
catch(Exception e){System.out.println(>> ERR:+ e); }
System.out.println(============ \\\
使用almostGoodMethod(+ bigSampleArray.getClass()。getSimpleName()+len:+ bigSampleArray.length + )......);
尝试
{
array = testType.nearlyGoodMethod(bigSampleArray);
testType.executeTests(array);
}
catch(Exception e){System.out.println(>> ERR:+ e); }
System.out.println(============ \\\
使用bestMethod(+ smallSampleArray.getClass()。getSimpleName()+len:+ smallSampleArray.length + )......);
尝试
{
array = testType.bestMethod(smallSampleArray);
testType.executeTests(array);
}
catch(Exception e){System.out.println(>> ERR:+ e); }
System.out.println(============ \\\
使用bestMethod(+ bigSampleArray.getClass()。getSimpleName()+len:+ bigSampleArray.length + )......);
尝试
{
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< A> {
private int testSize = 7;
final Class< A> clazz中;
public A [] badMethod()
{
System.out.println(这将在尝试返回数组时抛出一个ClassCastException,因为Object不是一种 + clazz.getSimpleName());
A [] array =(A [])new Object [testSize]; //警告:类型安全:未检查从Object []转换为A []
System.out.println(Array.getClass()。getComponentType()+created);
返回数组;
$ b public A [] alsoBadMethod(A sampleType)
{
System.out.println(Will not respect A type(+ clazz.getSimpleName( )+),将始终使用sampleType中的最高类型,并告诉它是A []但它不是,在这种情况下,将返回+ sampleType.getClass()。getSimpleName()+[]并表示它是+ clazz.getSimpleName()+[] while developing);
A [] array =(A [])Array.newInstance(sampleType.getClass(),testSize); //类型安全性:未检查从Object转换为A []
返回数组;
$ b public A [] almostGoodMethod(A [] array)
{
System.out.println(唯一的保证是返回的数组是(+ clazz.getSimpleName()+),所以返回的类型不清晰,可能是A或者在参数中传递的类型,但会告诉它是A [],但可能不是); (array.length< testSize)
array =(A [])Array.newInstance(array.getClass()。getComponentType(),testSize); //类型安全性:未检查从Object转换为A []
System.out.println(在这种情况下:+ array.getClass()。getComponentType()。getSimpleName()+[],expecting: + clazz.getSimpleName()+[]);
返回数组;
}
public< T extends A> T [] bestMethod(T [] array)
{
System.out.println(它保证返回与样本数组相同类型的数组,并且它必须是A的一个实例,所以,这是最好的方法);
if(array.length< testSize)
array =(T [])Array.newInstance(array.getClass()。getComponentType(),testSize); //类型安全性:未检查从Object转换为T []
System.out.println(this case:+ array.getClass()。getComponentType()。getSimpleName()+[],expected: + array.getClass()getComponentType()getSimpleName()+[]);
返回数组;
$ b $ 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));
$ b $ public void tryToSet(A [] array,int index,Object value)
{
System.out.println(试图设置+ value + ()+array.getClass()。getComponentType()。getSimpleName());(+ + value.getClass()。getSimpleName()+)at+ index +。
尝试
{
if(array instanceof Object [])((Object [])array)[index] = value;
else array [index] =(A)value; //类型安全性:未检查从Object转换为A
System.out.println(## OK:Success:+ array.getClass()。getComponentType()。getSimpleName()+[+ index + ] =+ array [index]);
}
catch(Exception e){System.out.println(>> ERR:+ e); }
}
}
}
这里是测试结果...您可以看到
bestMethod
始终返回预期结果。
Some developers create arrays of generic type by creating an
Object[]
and casting to the generic type as in this example code:public class ArrTest<E> { 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<String> t = new ArrTest<String>(); t.test("Hello World"); } }
That example will work and just has a warning:
Type safety: Unchecked cast from Object[] to E[]
.Is it discouraged? Is this the best way to create an array of a generic type? Can this cause unexpected results or exceptions if I use this object extensively in my software?
解决方案In the question's example the
b
variable is not aString[]
even though we casted it toE[]
and defined thatE
isString
while constructing the instance. It's anObject[]
. That happens because Java doesn't know what typeE
is at runtime because, in this example, we didn't define a parent class forE
. So, it will automatically haveObject
as its parent.In other terms,
public class ArrTest<E>
is identical topublic class ArrTest<E extends Object>
.Java doesn't know what
E
is at the runtime because it'sunchecked
.Unchecked
means Java will not check if theE
type is an extension or implementation of the defined parent class. So, the only thing Java knows aboutE
at runtime is that<E extends Object>
.Therefore
E[] b = (E[]) new Object[1];
will execute as
Object[] b = (Object[]) new Object[1];
That's why the example will not throw a
ClassCastException
and will confuse the developer.If we try to use
b
as a realString[]
then Java will throw aClassCastException
since Java sees it as anObject[]
. For example, if we change the method to: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<String> t = new ArrTest<String>(); String[] result = t.test("Hello World"); }
Now we will receive a
ClassCastException
inString[] result
because the returned type will beObject[]
and we are trying to store it in aString[]
variable. Java will see the type difference and throw the exception.That's why casting
Object[]
to a generic array is discouraged, it only leads to confusion.Before writing this answer, I created a test case with some possible ways to create a generic array and I concluded that this is the best method:
public class ExampleType<A extends Number>{ public <T extends A> 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; } }
It's guaranteed to return an array of the same type as the array passed as an argument and it must be an instance of
A
defined in theExampleType<A extends Number>
. If you create anExampleType
ofInteger
you will need to use anInteger[]
as the argument. If you don't want an array ofInteger
specifically but you want to store any type of number you could use aNumber[]
as the argument.If you don't need generic types in the class you could simplify it to:
public <T> T[] bestMethod(T[] array)
Of if you want it to return subclasses of
Number
only:public <T extends Number> T[] bestMethod(T[] array)
Here is my test case if you want to test it yourself:
public class Test { public static class ArrTest<E> { 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<String> t = new ArrTest<String>(); 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>(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>(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>(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>(Number.class), new CustomNumberB(3L), new CustomNumberA[0], new CustomNumberA[10]); } public static <A extends Number> void test(ExampleType<A> testType, A sampleData, A[] smallSampleArray, A[] bigSampleArray) { Class<A> 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<A>{ private int testSize = 7; final Class<A> 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 extends A> 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); } } } }
And here are the test results... You can see that the
bestMethod
always returns the expected result.这篇关于为什么不创建一个Object []并转换为泛型?什么是解决方案?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!