3

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

John Ericksen
  • 10,995
  • 4
  • 45
  • 75
  • possible duplicate of [List class's toArray in Java- Why can't I convert a list of "Integer" to an "Integer" array?](http://stackoverflow.com/questions/7281793/list-classs-toarray-in-java-why-cant-i-convert-a-list-of-integer-to-an-int) – J-16 SDiZ May 18 '12 at 16:13
  • I actually read that post before submitting my post. My question is not a duplicate because I am asking about the generics related to arrays. – John Ericksen May 18 '12 at 16:26

5 Answers5

2

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!

2

Don't try to cast to T, try casting to T[], as you are handling an array of T's.

Hidde
  • 11,493
  • 8
  • 43
  • 68
2

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);
        }
    };
}
Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
  • That's a sweet solution. And much nicer than having to have a `convert()` routine with a big `if/else` for all the types you want to handle. – QuantumMechanic May 18 '12 at 16:25
  • Tom, here's my problem, I don't have control over the return type of the method as I am implementing an interface. Here's the relevant code: https://github.com/johncarl81/transfuse/blob/master/transfuse/src/main/java/org/androidtransfuse/analysis/adapter/AnnotationTypeValueConverterVisitor.java – John Ericksen May 18 '12 at 16:33
  • @TomHawtin-tackline what do you mean "starting from there"? Do you have a better solution in mind? – John Ericksen May 18 '12 at 16:41
  • This made me take a step back find a solution using a similar technique you described. Thanks. – John Ericksen May 18 '12 at 18:44
1

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;
        }

    }
}
QuantumMechanic
  • 13,795
  • 4
  • 45
  • 66
  • Very similar to what @Hidde was suggesting. – John Ericksen May 18 '12 at 16:21
  • As you can see from the concrete example I added to the question, I not have control over the return type of the TypeConverter (AnnotationTypeValueConverterVisitor) as I am actually implementing an interface. Suggestions? – John Ericksen May 18 '12 at 16:37
0

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

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

J-16 SDiZ
  • 26,473
  • 4
  • 65
  • 84