我有这堂课:
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);
这是我在实例化后看到的:
这怎么可能?
另外,如何从实例化代码中确保使用正确类型的列表?
答案 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();