How to properly return generic array in Java generic method?

前端 未结 3 387
逝去的感伤
逝去的感伤 2021-02-03 12:09

I have below generic method that returns a generic array:

public static  T[] genericMethod1(List input) {
    T[] res = (T[]) new Object[input.s         


        
相关标签:
3条回答
  • 2021-02-03 12:14

    Another way is to do it like in java.util.ArrayList.toArray(T[]). You pass the type of Array to that Method, if it's big enough it will be reused, otherwise an new Array is generated.

    Example:

        List<Integer> intList = new ArrayList<>();
        intList.add(Integer.valueOf(1));
        intList.add(Integer.valueOf(2));
        intList.add(Integer.valueOf(3));
        Integer[] array = intList.toArray(new Integer[] {});
        System.out.println(Arrays.toString(array));//Will Print [1, 2, 3]
    

    Implementation of ArrayList.toArray(T[]) see here.

    0 讨论(0)
  • 2021-02-03 12:21

    The explanation for what you are seeing is due to something called type erasure. Here is what your genericMethod() will look like after the compiler performs type erasure:

    public static Object[] genericMethod(List input) {
        Object[] res = new Object[input.size()];
    
        int i = 0;
        for (Object t : input) {
            res[i] = t;
            i++;
        }
        return res;
    }
    

    In other words, this method will return an array of type Object. There is no way to cast an Object[] to an Integer[] because they are not the same type. If you want your method to be able to dynamically return the type you want, then you can use Array.newInstance(). This will require also passing in the type of the array you want as an input parameter:

    public static <T> T[] genericMethod(Class<T> clazz, List<T> input) {
        @SuppressWarnings("unchecked")
        T[] res = (T[]) Array.newInstance(clazz, input.size());
    
        int i = 0;
        for (T t : input) {
            res[i] = t;
            i++;
        }
        return res;
    }
    

    Now your code snippet will run without error:

    LinkedList<Integer> list = new LinkedList<Integer>();    
    Integer[] i = genericMethod(Integer.class, list);
    

    Update:

    Your second method, genericMethod2(), will look like this after type erasure:

    public static Object genericMethod2(List input) {
        return input.get(0);
    }
    

    It will return the first element of the input list, cast to Object. Here is your usage of that method:

    Integer j = genericMethod2(list);
    

    The compiler will try to cast the output from genericMethod2() to Integer:

    Integer j = (Integer)genericMethod2(list);
    

    This cast is legal, because every Integer is also an Object, and furthermore it succeeds here because you passed in a collection of Integer. This second method is not the same scenario as the first one you highlighted for us.

    0 讨论(0)
  • 2021-02-03 12:26

    When calling the method, genericMethod you are assuming that it returns array of integers, which is NOT correct. It actually returns array of type Object at runtime.

        List<Integer> input = new ArrayList<Integer>();
        input.add(1);
        Object[] output = genericMethod(input);
        for(Object obj : output){
            System.out.println("Value= "+ (Integer)obj);
        }
    

    So we need to cast the individual content of the array.

    One general guidline is that we shouldn't mix ARRAY and GENERICS in Java.

    Update:

    Reference from Effective Java:

    In Summary, arrays and generics have very different type rules. Arrays are covariant and reified; generics are invariant and erased. As a consequcne, arrays provide runtime type safety but not compile-time type safety and vice versa for generics. Generally speaking, arrays and generics don’t mix well. If you find yourself mixing them and getting compile-time error or warnings, your first impulse should be to replace the arrays with lists.

    0 讨论(0)
提交回复
热议问题