How can I insert an ArrayList <String> into a constructor that takes a List <Integer>?
I have this class:
public class TestSubject { public TestSubject(List<Integer> list) { } } I create it and somehow work, although I am inserting an ArrayList<String> into the constructor, which takes a List<Integer> :
List<String> strings = new ArrayList<>(); strings.add("foo"); Constructor<TestSubject> constructor = TestSubject.class.getConstructor(List.class); TestSubject test = constructor.newInstance(strings); This is what I see after instantiating:
How is this possible?
Also, how can I make sure that the instance code is used in the correct list type?
This is due to the type of erasure . Because <Integer> will be erased, and there will only be List . However, you create an instance with reflection, and at runtime it will not check the List type.
Here the type of the list is checked at compile time if you create it with new , but in this case you skipped compile time checking and at runtime it is really due to type erasure.
In your case, there is no direct way, besides checking the type of elements manually in the constructor.
Answering the last part of the question. In fact, you can check if there is a valid type. You can get a parameter of type generic for a constructor argument like this
// 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]; This also works for fields if the list parameter is not general.
There is one more trick. You can save the general parameter using a type reference using an anonymous class:
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; } } So, here is a very simple idea how you can achieve your goal. You can save the general parameter using a type reference and check the argument as follows:
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(); 