为什么不创建一个Object []并转换为泛型?什么是解决方案? [英] Why not create an Object[] and cast to a generic type? What's the solution?

查看:169
本文介绍了为什么不创建一个Object []并转换为泛型?什么是解决方案?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有些开发人员通过创建 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> 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 Object [] ,所以我们试图存储它,因为$ c>在 String [] result String [] 变量中。 Java会看到类型差异并抛出异常。



这就是为什么将 Object [] 转换为泛型数组的原因不鼓励,它只会导致混淆。



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

  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); //类型安全性:未检查从Object转换为T []
System.out.println(在这种情况下:+ array.getClass()。getComponentType()。getSimpleName());
返回数组;




$ b

它保证返回一个相同类型的数组该数组作为参数传递,它必须是 ExampleType< A extends Number> 中定义的 A 的实例。如果创建整数 ExampleType ,则需要使用 Integer [] 作为参数。如果你不需要一个 Integer 的数组,但是你想存储任何类型的数字,你可以使用 Number [] $ b 如果你不需要类中的泛型类型,你可以将它简化为:

  public< T> T [] bestMethod(T [] array)

如果您希望它返回<$的子类c $ c> Number only:

  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 始终返回预期结果。

http://pastebin.com/CxBSHaYm


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 a String[] even though we casted it to E[] and defined that E is String while constructing the instance. It's an Object[]. That happens because Java doesn't know what type E is at runtime because, in this example, we didn't define a parent class for E. So, it will automatically have Object as its parent.

In other terms, public class ArrTest<E> is identical to public class ArrTest<E extends Object>.

Java doesn't know what E is at the runtime because it's unchecked. Unchecked means Java will not check if the E type is an extension or implementation of the defined parent class. So, the only thing Java knows about E 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 real String[] then Java will throw a ClassCastException since Java sees it as an Object[]. 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 in String[] result because the returned type will be Object[] and we are trying to store it in a String[] 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 the ExampleType<A extends Number>. If you create an ExampleType of Integer you will need to use an Integer[] as the argument. If you don't want an array of Integer specifically but you want to store any type of number you could use a Number[] 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.

http://pastebin.com/CxBSHaYm

这篇关于为什么不创建一个Object []并转换为泛型?什么是解决方案?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆