我如何能够将ArrayList <string>插入到接受List <integer>的构造函数中?

时间:2016-08-16 16:44:46

标签: java generics types constructor

我有这堂课:

public class TestSubject {
    public TestSubject(List<Integer> list) {
    }
}

我正在以这样的方式实例化它并且不知何故它正在工作,即使我将ArrayList<String>插入到接受List<Integer>的构造函数中:

List<String> strings = new ArrayList<>();
strings.add("foo");
Constructor<TestSubject> constructor = TestSubject.class.getConstructor(List.class);
TestSubject test = constructor.newInstance(strings);

这是我在实例化后看到的:

enter image description here

这怎么可能?

另外,如何从实例化代码中确保使用正确类型的列表?

2 个答案:

答案 0 :(得分:3)

由于type erasure而发生这种情况。由于<Integer>将被删除,因此仅List。但是,您使用反射创建实例,并且在运行时它将检查List的类型。

如果使用new创建列表,则在编译期间检查列表的类型,但在这种情况下,您跳过编译时检查,并且在运行时它因类型擦除而有效。

在您的情况下,除了在构造函数中手动检查元素的类型之外,没有直接的方法。

答案 1 :(得分:0)

回答最后一部分问题。实际上你可以检查是否传递了正确的类型。您可以获取构造函数参数的泛型类型参数,例如

// in your case that will give you parametrized type
// java.util.List<java.lang.Integer>
Type type = constructor.getGenericParameterTypes()[0];

ParameterizedType argumentType = (ParameterizedType) type;
// that will give you List type parameter - java.lang.Integer
Type argumentType = type[0];

这也适用于字段,除非列表参数不是通用的

还有另一招。您可以使用匿名类使用类型引用存储泛型参数:

public abstract class TypeReference<T> {
    private final Type type;

    public TypeReference() {
        if (!getClass().isAnonymousClass()) {
            throw new IllegalArgumentException(getClass() + " should be anonymous");
        }

        final Type superClass = getClass().getGenericSuperclass();
        if (!(superClass instanceof ParameterizedType)) {
            throw new IllegalArgumentException("missing type parameter due to type erasure");
        }

        this.type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
    }

    public final Type getType() {
        return this.type;
    }
}

因此,如何实现目标是非常基本的想法。您可以使用类型引用保留泛型参数,并检查参数,如下所示:

public class ObjectBuilder<T> {
    List<Object> validatedArguments = new ArrayList<>();            
    Constructor<T> ctor = /*... */;


    public void <A> addArgument(A argument
                                TypeReference<A> argumentType) {
         int currentArgument = validatedArguments.size();
         Type ctorArgumentType = 
             ctor.getGenericParameterTypes()[currentArgument]/* */;
         Type argumentType = argumentType.getType();
         // compare it carefully!

         validatedArguments.add(argument);
    }

    public T build() {
       // new instance creating ...
    }
}

ObjectBuilder<TestSubject> subject = new ObjectBuilder<>();
subject.addArgument(list, new TypeReference<List<Integer>>() {})
TestSubject obj = subject.build();