简体   繁体   中英

Java Generics and Array types

Ran into an issue with generics and array types that I am unable to solve. It boils down to this. In the following code, how can I convert a generic List into an Array of the same generic type, while using a factory method ("T convert(String value)") to convert each individual element of the input generic List:

@Test
public void test(){
    List<String> integers = Arrays.asList("1", "2", "3", "4");

    Integer[] integerArray = new TypeConverter<Integer[]>(Integer[].class).convert(integers);

    assertEquals(4, integerArray.length);
    assertEquals(1, integerArray[0].intValue());
    assertEquals(2, integerArray[1].intValue());
    assertEquals(3, integerArray[2].intValue());
    assertEquals(4, integerArray[3].intValue());
}

public class TypeConverter<T>{
    Class<T> type;

    public TypeConverter(Class<T> type) {
        this.type = type;
    }

    T convert(List<String> values){

        List output = new ArrayList();

        for (String value : values) {
            //have to use Object.class here since I cant get the non-array type of T:
            output.add(new TypeConverter(type.getComponentType()).convert(value));
        }

        return (T) output.toArray();
    }

    T convert(String value){
        //convert to an int;
        if(type == Integer.class){
            return (T) new Integer(Integer.parseInt(value));
        }
        return null;
    }
}

As you can see, my naive approach was to simply use the toArray Method, and cast the result like so:

(T) value.toArray()

but this results in a ClassCastException:

java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer

Is there a way to solve this that I am not seeing or should I take another approach?

Edit

Here's the concrete code that I am trying to fix. Specifically the visitArray() method:

https://github.com/johncarl81/transfuse/blob/master/transfuse/src/main/java/org/androidtransfuse/analysis/adapter/AnnotationTypeValueConverterVisitor.java

You may use the alternate version of List.toArray that takes as a parameter the type of the array you want to get.

toArray Method

You may create an empty array with some method of the Array class.

Array.newInstance

So having the expected type you just use Array.newInstance(type, 0); and pass in the result to the toArray method.

Edit: Since your generic type is an array, you need to get the type of the components, try this:

Object[] array = (Object[]) Array.newInstance(type.getComponentType(), 0);
return (T) output.toArray(array);

Your value conversion method has a little something I'll let you figure out how to solve :)

Cheers!

不要尝试强制转换为T ,尝试强制转换为T[] ,因为您正在处理T的数组。

I suggest ditching the reflection and reference arrays.

Slightly abusing inheritance:

public abstract class StringConverter<T> {

    public List<T> convert(List<String> values) {
        List<T> output = new ArrayList<>();
        for (String value : values) {
            output.add(convert(value));
        }
        return output;
    }

    public abstract T convert(String value);
}

public static StringConverter<Integer> toInteger() {
    return new StringConverter<>() {
        public Integer convert(String value) {
             return Integer.parseInt(value);
        }
    };
}

This works for me:

import java.util.*;
import java.lang.reflect.*;

public class Foo {
    public static void main(String[] args) {
        new Foo().test();
    }

    public void test(){
        List<String> integers = Arrays.asList("1", "2", "3", "4");

        Integer[] integerArray = new TypeConverter<Integer>(Integer.class).convert(integers);

        System.out.println(Arrays.deepToString(integerArray));

    }

    public class TypeConverter<T>{
        Class<T> type;

        public TypeConverter(Class<T> type) {
            this.type = type;
        }

        T[] convert(List<String> values){

            List<T> output = new ArrayList<>();

            for (String value : values) {
                output.add(convert(value));
            }

            return output.toArray((T[]) Array.newInstance(type, output.size()));
        }

        T convert(String value){
            if(type == Integer.class){
                return (T) new Integer(Integer.parseInt(value));
            }
            else if(type == Long.class){
                return (T) new Long(Long.parseLong(value));
            }
            return null;
        }

    }
}

return (T) values.toArray(); should be return (T)output.toArray( new Integer[0])

no, you have to hard code new Integer[0]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM