Java 为什么不创建一个对象[]并强制转换为泛型类型?什么';解决办法是什么?
一些开发人员通过创建Java 为什么不创建一个对象[]并强制转换为泛型类型?什么';解决办法是什么?,java,generics,Java,Generics,一些开发人员通过创建对象[]并强制转换为泛型类型来创建泛型类型的数组,如下示例代码所示: 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>
对象[]
并强制转换为泛型类型来创建泛型类型的数组,如下示例代码所示:
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");
}
}
公共类测试{
公开无效测试(EA){
E[]b=(E[])新对象[1];
b[0]=a;
System.out.println(b[0]);
}
公共静态void main(字符串[]args){
ArrTest t=新的ArrTest();
t、 测试(“你好世界”);
}
}
该示例将起作用,只是有一个警告:Type safety:Unchecked cast from Object[]to E[]
这是否令人沮丧?这是创建泛型类型数组的最佳方法吗?如果我在我的软件中广泛使用此对象,这会导致意外的结果或异常吗?在问题的示例中,
b
变量不是String[]
,即使我们在构造实例时将其强制转换为E[]
,并定义E
是String
。它是一个对象[]
。这是因为Java不知道运行时E
是什么类型,因为在本例中,我们没有为E
定义父类。因此,它将自动将对象
作为其父对象
换句话说,公共类ArrTest
与公共类ArrTest
相同
Java不知道运行时E
是什么,因为它是未选中的
Unchecked
表示Java不会检查E
类型是否是已定义父类的扩展或实现。因此,Java在运行时只知道E
所以
E[]b=(E[])新对象[1]代码>
将作为
Object[]b=(Object[])新对象[1]代码>
这就是为什么这个示例不会抛出ClassCastException
,并且会让开发人员感到困惑
如果我们尝试将b
用作真正的字符串[]
,那么Java将抛出ClassCastException
,因为Java将其视为对象[]
。例如,如果我们将方法更改为:
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");
}
它保证返回一个与作为参数传递的数组类型相同的数组,并且它必须是ExampleType中定义的A
的实例。您得到的警告仅说明编译器无法根据Java语言规范指定的规则确保静态类型安全。换句话说,它指出静态类型的安全性已经被破坏
然而,这并没有使这个成语明确地受到劝阻。以下是一个完全合法的案例(Grepcode格式):
@(“未选中”)
公共T[]数组(T[]a){
如果(a.length 创建一个a的运行时类型的新数组,但我的内容:
返回(T[])数组。(elementData,size,a.();
系统。(elementData,0,a,0,size);
如果(a.length>size)
a[size]=null;
返回a;
尽管使用的向下转换未经检查,但更高级别的逻辑清楚地表明它是类型安全的。我创建此自我回答主要是为了回答以下评论:基本上,您是从超类转换到子类。就像创造一个动物和铸造猫。对于数组来说,这是可行的,因为不管对象数组的类型如何,它的行为基本相同,但是cast操作仍然会在运行时抛出异常(至少在更一般的非泛型情况下——泛型会导致cast没有操作)。但是,如果您返回了对象,编译器将执行隐式的checkcast
,您将得到位。请注意,为了真正激发您的答案,您需要一个示例,其中您的泛型数组将脱离其创建范围。只有到那时,它才开始出现类型错误问题。但是,无论是在您的示例中还是在您所链接的问题的示例中,都没有出现这种情况。我说过不鼓励创建对象[]
,直接将其转换为t[]
,而不进行检查,这实际上并不是不鼓励,而是错误的,可能会出现无法工作的情况,您提供的案例不会这样做,另外,Arrays.copyOf()
在强制转换之前检查T[]是否是Object[]的实例,如果不是Object[],则使用反射,就像我的回答一样<代码>T[]复制=((对象)新类型==(对象)对象[]。类)?(T[])新对象[newLength]:(T[])数组.newInstance(newType.getComponentType(),newLength)代码>如果按字面意思考虑,您的问题会问:“为什么不鼓励编写不可能在运行时或编译时导致任何类型错误的向下转换?”。我选择将其解释为范围更广。我说不鼓励创建对象[]并在不检查的情况下将其直接转换到T[]——不,你没有这样说,也不适用于你的示例。为什么您可能想检查由新对象[]
创建的已知对象的类型?就像我的回答一样
——请注意,我在这里回答一个问题,而不是评论其他人的答案。是的,我实际上没有说,你是对的。。我评论了我的答案,因为我说过最好的解决方案是通过反射,这是在Arrays.copyOf中完成的,它在创建对象[]并强制转换到T[]之前检查泛型类型(T[])是否是对象[],否则我在答案中解释的问题可能会发生。你的回答还不错,我问得很糟糕,我会编辑这个问题,更清楚地说“这就是为什么这个示例不会抛出ClassCastException,会让开发人员感到困惑的原因。”如果它完全按照开发人员的预期工作,那么这会让人感到困惑吗?@PeterLawrey为什么有人会将Object[]转换为Object[],并说它是String[]?开发人员希望将对象[]强制转换为字符串[],但这是不可能的,因为该对象不是字符串的实例。你试过了吗?我试过了
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;
}
}
public <T> T[] bestMethod(T[] array)
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("#############\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); }
}
}
}
323 @SuppressWarnings("unchecked")
324 public <T> T[] toArray(T[] a) {
325 if (a.length < size)
326 // Make a new array of a's runtime type, but my contents:
327 return (T[]) Arrays.copyOf(elementData, size, a.getClass());
328 System.arraycopy(elementData, 0, a, 0, size);
329 if (a.length > size)
330 a[size] = null;
331 return a;
332 }