Lets say I have a parent class and a child class which extends my parent and I have the following code.
public class SomeClass {
private Parent myParent;
public SomeClass(Parent myParent) {
this.myParent = myParent;
}
}
Why is this allowed
Class<SomeClass> clazz = SomeClass.class;
SomeClass someClass = clazz.getConstructor(Parent.class).getInstance(Child);
and this isnt't
Class<SomeClass> clazz = SomeClass.class;
SomeClass someClass = clazz.getConstructor(Child.class).getInstance(Child);
The second one throws an NoSuchMethodeException. Why isn't there dynamic binding in this case but when using the normal Constructor dynamic binding works just fine? And is there a way to workaround this problem?
Edit:
I'm trying to load jar files at runtime. At this moment I have the classes I need, loaded with an URLClassLoader
. Next I want to create a new instance of one of the loaded classes. To create the instance i call urlClassLoader.loadClass(nameOfClass).getConstructor(parameterType).newInstance(initArguments);
The parameterType
would be child.class
in this case.
The method getConstructorAcceptingSupertype
below finds a constructor that accepts the given parameter type or one of its supertypes.
import java.lang.reflect.Constructor;
public class ConstructorTester {
public ConstructorTester(Object o) {
System.out.println("Object constructor");
}
public ConstructorTester(CharSequence o) {
System.out.println("CharSequence constructor");
}
public static void main(String[] args) throws Throwable {
Object[] arg = {null};
getConstructorAcceptingSupertype(ConstructorTester.class, Object.class).newInstance(arg); //Object constructor
getConstructorAcceptingSupertype(ConstructorTester.class, CharSequence.class).newInstance(arg); //CharSeq constructor
getConstructorAcceptingSupertype(ConstructorTester.class, Integer.class).newInstance(arg); //Object constructor
getConstructorAcceptingSupertype(ConstructorTester.class, String.class).newInstance(arg); //CharSeq constructor
getConstructorAcceptingSupertype(ConstructorTester.class, StringBuilder.class).newInstance(arg); //CharSeq constructor
}
/**
* Returns a one-arg {@link Constructor} from {@code clazz} that accepts the given {@code parameterType}. This
* method guarantees that there is no other one-arg constructor in {@code clazz} that accepts a superclass or
* superinterface of the type that the returned constructor accepts.
*/
private static Constructor<?> getConstructorAcceptingSupertype(Class<?> clazz, Class<?> parameterType) {
Constructor<?> correctConstructor = null;
for(Constructor<?> constructor : clazz.getConstructors()) {
if( constructor.getParameterCount() == 1 &&
constructor.getParameters()[0].getType().isAssignableFrom(parameterType)) {
if(correctConstructor == null) {
correctConstructor = constructor;
}
else { //see if this constructor is more specific than the current correctConstructor.
Class<?> currentType = correctConstructor.getParameters()[0].getType();
Class<?> newType = constructor.getParameters()[0].getType();
if(currentType.isAssignableFrom(newType))
correctConstructor = constructor;
}
}
}
if(correctConstructor == null)
throw new IllegalArgumentException("No one-arg constructor exists that accepts the given parameter type");
return correctConstructor;
}
}
Note that there is no way to avoid an ambiguous situation where an interface, say C
, extends two other interfaces A
and B
, and your class has two constructors (SomeClass(A)
and SomeClass(B)
). In such a scenario, even an explicit constructor invocation such as new SomeClass(instanceOfC)
would fail at compile-time. In such a scenario, My method above will return an arbitrary constructor (either SomeClass(A)
or SomeClass(B)
), but does not guarantee which.