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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为什么不创建一个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");
  }
}

这个例子可行,只是有一个警告:Type safety: Unchecked cast from Object[] to E[]

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

答案

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

换句话说,public class ArrTest<E>public class ArrTest<E extends Object>相同。

Java不知道E在运行时是什么,因为它是uncheckedUnchecked表示Java不会检查E类型是否是已定义父类的扩展或实现。因此,Java在运行时唯一了解E就是<E extends Object>

因此

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<String> t = new ArrTest<String>();
    String[] result = t.test("Hello World");
}

现在我们将在ClassCastException收到一个String[] result,因为返回的类型将是Object[],我们正试图将它存储在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); //Type safety: Unchecked cast from Object to T[]
        System.out.println("in this case: "+array.getClass().getComponentType().getSimpleName());
        return array;
    }
}

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

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

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

如果您希望它仅返回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("#############\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;

以上是关于为什么不创建一个Object []并转换为泛型类型?解决方案是什么?的主要内容,如果未能解决你的问题,请参考以下文章

无法将类型“Int”的值转换为泛型中的预期参数类型“Int”

将返回值转换为泛型类型

在 C# 中转换为泛型类型

Java泛型详解

将返回值转换为泛型类型

为泛型类型编写 Diesel CRUD 操作