为什么 class.getConstructor(parameters) 不允许子对象作为参数?
Why does class.getConstructor(parameters) not allow child objects as parameters?
假设我有一个父项 class 和一个扩展我的父项的子项 class,我有以下代码。
public class SomeClass {
private Parent myParent;
public SomeClass(Parent myParent) {
this.myParent = myParent;
}
}
为什么允许这样做
Class<SomeClass> clazz = SomeClass.class;
SomeClass someClass = clazz.getConstructor(Parent.class).getInstance(Child);
这不是
Class<SomeClass> clazz = SomeClass.class;
SomeClass someClass = clazz.getConstructor(Child.class).getInstance(Child);
第二个抛出 NoSuchMethodeException。为什么在这种情况下没有动态绑定,但在使用普通构造函数时动态绑定工作得很好?
有没有办法解决这个问题?
编辑:
我试图在运行时加载 jar 文件。现在我有我需要的 classes,加载了 URLClassLoader
。接下来,我想创建一个已加载 classes 的新实例。要创建实例,我调用 urlClassLoader.loadClass(nameOfClass).getConstructor(parameterType).newInstance(initArguments);
在这种情况下,parameterType
将是 child.class
。
下面的方法 getConstructorAcceptingSupertype
查找接受给定参数类型或其超类型之一的构造函数。
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;
}
}
请注意,无法避免模棱两可的情况,例如接口 C
扩展了另外两个接口 A
和 B
,而您的 class有两个构造函数(SomeClass(A)
和 SomeClass(B)
)。在这种情况下,即使 new SomeClass(instanceOfC)
之类的显式构造函数调用也会在编译时失败。在这种情况下,我上面的方法将 return 任意构造函数(SomeClass(A)
或 SomeClass(B)
),但不保证是哪个。
问题 w.l.o.g. 无法解决,因为我们可以创建模棱两可的示例。考虑以下示例:
interface Foo {}
class Bar {
public Bar() {}
public Bar(Bar other) {}
public Bar(Foo other) {}
}
class Baz extends Bar implements Foo {}
如果我们现在调用new Bar(new Baz())
,程序将无法编译。
我强烈建议不要实施在上述情况下为我们“选择”构造函数的解决方案。相反,我会选择:
- 要么抛出一些
Exception
,表示无法确定单个构造函数,要么
- return一个
List<Constructor<?>>
.
对于第二种方法(returning a List<...>
),我们可以使用以下代码:
public static List<Constructor<?>> getConstructorsFromTypeFittingActualParameters(
Class<?> type,
Class<?>... actualParameters) {
final Class<?>[] actualParametersNotNull =
Optional.ofNullable(actualParameters).orElseGet(() -> new Class<?>[0]);
return Arrays.stream(type.getConstructors())
.filter(constructor -> constructor.getParameterCount() == actualParametersNotNull.length)
.filter(constructor -> formalParameterTypesAcceptActualParameterTypes(
constructor.getParameterTypes(),
actualParametersNotNull))
.collect(Collectors.toList());
}
private static boolean formalParameterTypesAcceptActualParameterTypes(
Class<?>[] formalParameterTypes,
Class<?>[] actualParameterTypes) {
Objects.requireNonNull(formalParameterTypes);
Objects.requireNonNull(actualParameterTypes);
if (formalParameterTypes.length != actualParameterTypes.length) {
throw new IllegalArgumentException();
}
for (int index = 0; index < formalParameterTypes.length; ++index) {
if (!formalParameterTypes[index].isAssignableFrom(actualParameterTypes[index])) {
return false;
}
}
return true;
}
鉴于上面的示例,此实现将 return 两个构造函数。
假设我有一个父项 class 和一个扩展我的父项的子项 class,我有以下代码。
public class SomeClass {
private Parent myParent;
public SomeClass(Parent myParent) {
this.myParent = myParent;
}
}
为什么允许这样做
Class<SomeClass> clazz = SomeClass.class;
SomeClass someClass = clazz.getConstructor(Parent.class).getInstance(Child);
这不是
Class<SomeClass> clazz = SomeClass.class;
SomeClass someClass = clazz.getConstructor(Child.class).getInstance(Child);
第二个抛出 NoSuchMethodeException。为什么在这种情况下没有动态绑定,但在使用普通构造函数时动态绑定工作得很好? 有没有办法解决这个问题?
编辑:
我试图在运行时加载 jar 文件。现在我有我需要的 classes,加载了 URLClassLoader
。接下来,我想创建一个已加载 classes 的新实例。要创建实例,我调用 urlClassLoader.loadClass(nameOfClass).getConstructor(parameterType).newInstance(initArguments);
在这种情况下,parameterType
将是 child.class
。
下面的方法 getConstructorAcceptingSupertype
查找接受给定参数类型或其超类型之一的构造函数。
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;
}
}
请注意,无法避免模棱两可的情况,例如接口 C
扩展了另外两个接口 A
和 B
,而您的 class有两个构造函数(SomeClass(A)
和 SomeClass(B)
)。在这种情况下,即使 new SomeClass(instanceOfC)
之类的显式构造函数调用也会在编译时失败。在这种情况下,我上面的方法将 return 任意构造函数(SomeClass(A)
或 SomeClass(B)
),但不保证是哪个。
问题 w.l.o.g. 无法解决,因为我们可以创建模棱两可的示例。考虑以下示例:
interface Foo {}
class Bar {
public Bar() {}
public Bar(Bar other) {}
public Bar(Foo other) {}
}
class Baz extends Bar implements Foo {}
如果我们现在调用new Bar(new Baz())
,程序将无法编译。
我强烈建议不要实施在上述情况下为我们“选择”构造函数的解决方案。相反,我会选择:
- 要么抛出一些
Exception
,表示无法确定单个构造函数,要么 - return一个
List<Constructor<?>>
.
对于第二种方法(returning a List<...>
),我们可以使用以下代码:
public static List<Constructor<?>> getConstructorsFromTypeFittingActualParameters(
Class<?> type,
Class<?>... actualParameters) {
final Class<?>[] actualParametersNotNull =
Optional.ofNullable(actualParameters).orElseGet(() -> new Class<?>[0]);
return Arrays.stream(type.getConstructors())
.filter(constructor -> constructor.getParameterCount() == actualParametersNotNull.length)
.filter(constructor -> formalParameterTypesAcceptActualParameterTypes(
constructor.getParameterTypes(),
actualParametersNotNull))
.collect(Collectors.toList());
}
private static boolean formalParameterTypesAcceptActualParameterTypes(
Class<?>[] formalParameterTypes,
Class<?>[] actualParameterTypes) {
Objects.requireNonNull(formalParameterTypes);
Objects.requireNonNull(actualParameterTypes);
if (formalParameterTypes.length != actualParameterTypes.length) {
throw new IllegalArgumentException();
}
for (int index = 0; index < formalParameterTypes.length; ++index) {
if (!formalParameterTypes[index].isAssignableFrom(actualParameterTypes[index])) {
return false;
}
}
return true;
}
鉴于上面的示例,此实现将 return 两个构造函数。