Java泛型和数组类型

3
遇到了一个与泛型和数组类型相关的问题,我无法解决。归结起来就是,在下面的代码中,我如何将一个泛型List转换为相同泛型类型的数组,同时使用工厂方法("T convert(String value)")将输入泛型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;
    }
}

正如您所看到的,我的天真做法是简单地使用toArray方法,并进行如下转换:

(T) value.toArray()

但是这将导致ClassCastException:

java.lang.ClassCastException:[Ljava.lang.Object;无法转换为[Ljava.lang.Integer

有没有我没看到的解决方法,或者我应该采取另一种方法?

编辑

这里是我试图修复的具体代码。特别是visitArray()方法:

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


我在提交我的帖子之前实际上阅读了那篇帖子。我的问题不是重复的,因为我正在询问与数组相关的泛型。 - John Ericksen
5个回答

2
您可以使用另一种版本的List.toArray,该版本需要一个参数,即您想要获取的数组类型。 toArray方法 您可以使用Array类的某些方法创建一个空数组。 Array.newInstance 因此,只需使用所期望的类型,然后将结果传递给toArray方法即可:Array.newInstance(type, 0);
编辑: 由于您的泛型类型是数组,您需要获取组件的类型,请尝试以下操作:
Object[] array = (Object[]) Array.newInstance(type.getComponentType(), 0);
return (T) output.toArray(array);

你的数值转换方法有一点小问题,我会让你自己想办法解决 :)

干杯!


我应该传入什么参数给toArray()方法? - John Ericksen
返回 (T) output.toArray(Array.newInstance(type, 0)); 无法编译。Arraay.newInstance(type, 0) 返回类型为 Object,而 List.toArray() 需要一个数组类型 T[]。 - John Ericksen
我会为您创建一个示例实现并编辑帖子。 - Juan Alberto López Cavallotti
你已经有了,你只需要修复字符串转整数的方法,以便测试通过。 - Juan Alberto López Cavallotti
太棒了!这在我的示例和我试图修复的代码中都非常有效。已经在转换方法中解决了那个问题。感谢Juan。 - John Ericksen

2

我建议放弃反射和引用数组。

稍微滥用继承:

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

这是一个很好的解决方案。比起必须要有一个包含所有你想处理的类型的大型if/else语句的convert()例程,这要好得多。 - QuantumMechanic
Tom,这是我的问题,由于我正在实现一个接口,所以我无法控制方法的返回类型。以下是相关代码:https://github.com/johncarl81/transfuse/blob/master/transfuse/src/main/java/org/androidtransfuse/analysis/adapter/AnnotationTypeValueConverterVisitor.java - John Ericksen
@TomHawtin-tackline 你说的“starting from there”是什么意思?你有更好的解决方案吗? - John Ericksen
这让我退后一步,使用你描述的类似技术找到了解决方案。谢谢。 - John Ericksen

2
不要尝试将类型转换为T,而是尝试将类型转换为T[],因为你正在处理一个T数组。

那是一种可能性,但我试图针对一个更通用的解决方案。 - John Ericksen

1
这对我有效:
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;
        }

    }
}

非常类似于@Hidde所建议的。 - John Ericksen
正如我在问题中添加的具体示例所示,由于我实际上是在实现一个接口,因此我无法控制TypeConverter(AnnotationTypeValueConverterVisitor)的返回类型。 有什么建议吗? - John Ericksen

0

return (T) values.toArray(); 应该改为 return (T)output.toArray( new Integer[0])

不,你必须硬编码new Integer[0]


基于类型T或Class<T>类型,我该如何做到这一点? - John Ericksen
Java的泛型无法做到这一点,你可以看到即使Java API也会使用技巧,如toArray(new Integer[0]) - J-16 SDiZ
在这个人为制造的例子中,硬编码的方法可以工作,但我需要一个更通用的解决方案。 - John Ericksen
不,唯一的替代方法是像“List.toArray(T [])”那样进行操作。也就是说,传入目标类型的数组..... 检查“List.toArray(T [])”的代码。 - J-16 SDiZ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接