为什么 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 扩展了另外两个接口 AB,而您的 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()),程序将无法编译。

Ideone demo

因此,Class.getConstructor(Class<?>... parameterTypes) interprets the types as formal parameters, not actual parameters.


我强烈建议不要实施在上述情况下为我们“选择”构造函数的解决方案。相反,我会选择:

  • 要么抛出一些 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 两个构造函数。

Ideone example