在我的程序中,我将JComponent
类注册到我的类中,以便为我的目的处理它们(将它们的值转换为设置条目)。它看起来像这样:
InputHandlers.register(InputJTextField.class, javax.swing.JPasswordField.class);
InputHandlers.register(InputJTextField.class, JTextField.class);
InputHandlers.register(InputJCheckBox.class, JCheckBox.class);
...
我将这些注册值保存到Map
并稍后检索。但是对于上面的示例,我遇到了一个问题:虽然javax.swing.JPasswordField.class
是JTextField.class
的子类型,但Class.getDeclaredConstructor
却看不到它。
我做了一个通用的例子,让这个问题更容易回答。考虑以下课程:
class A {
private final B b;
public A(B b) {
this.b = b;
}
}
class B {}
class C extends B {}
想象一下你想要这样做:
A.class.getDeclaredConstructor(C.class);
即使java.lang.NoSuchMethodException
是C
的子类型,也会抛出B
。这是完整的代码:
/**
* Test how Class.getDeclaredConstructor seeks for constructors.
* @author Jakub
*/
public class Constructors {
public static class A {
private final B b;
public A(B b) {
this.b = b;
}
}
public static class B {}
public static class C extends B {}
public static void main(String[] args) //throws Exception
{
/** TRY USING REFLECTION **/
//Make A from B
tryAfromParam(new B());
//Make A from C that is cast to B
tryAfromParam((B)new C());
//Make A from C without casting
tryAfromParam(new C());
}
public static A tryAfromParam(Object param) {
System.out.println("Try to make A from "+param.getClass()+" using A.class.getConstructor(...)");
try {
A a = AfromParam(param);
System.out.println(" Sucess :)");
return a;
} catch (Exception ex) {
System.out.println(" CONSTRUCTOR FAILED: "+ex);
}
return null;
}
public static A AfromParam(Object param) throws Exception {
//Fetch the A's class instance
Class cls = A.class;
//Define constructor parameters
Class[] arguments = new Class[] {
param.getClass()
};
//Try to get the constructor
Constructor<A> c = cls.getConstructor(arguments);
//Try to instantiate A
A a = c.newInstance(param);
//Return result
return a;
}
}
问题是:如何找到与参数或任何超类型兼容的构造函数?请注意new A(new C())
是有效的,因此反射应该以相同的方式工作 - 通常我想以Java调用它的方式调用构造函数。
答案 0 :(得分:4)
您可以使用java.beans.Expression:
C c;
A a = (A)new Expression(A.class, "new", new Object[]{c}).getValue();
答案 1 :(得分:2)
这就是所有(或大多数)反射行动的工作方式。他们期望并仅匹配声明的类型。在这种情况下,Class#getDeclareConstructor(Class...)
返回反映指定public的
Constructor
对象 此Class对象表示的类的构造函数。的的parameterTypes
参数是一个标识Class
个对象的数组 声明顺序的构造函数的形式参数类型。如果这Class
对象表示在非静态中声明的内部类 上下文,形式参数类型包括显式封闭 instance作为第一个参数。
如果您的参数类型是声明/正式类型的子类型,您需要检查自己。
您可以循环执行此操作
// Adjust for number of parameters
public static <T> Constructor<T> getConstructorDynamically(Class<T> clazz, Class<?> argumentType) {
while (argumentType != null) {
try {
return clazz.getDeclaredConstructor(argumentType);
} catch (NoSuchMethodException e) {
argumentType = argumentType.getSuperclass();
}
}
return null;
// or throw
}
我确信有一些库可以做到,但我现在找不到它们(例如,看看Spring)。
你也可以遍历构造函数并找到匹配的构造函数(这可能要快得多,因为你通常不会在类中声明那么多的构造函数)
@SuppressWarnings("unchecked")
public static <T> Constructor<T> findConstructor(Class<T> clazz, Class<?>[] argumentTypes) {
Constructor<T>[] constructors = (Constructor<T>[]) clazz.getDeclaredConstructors();
for (Constructor<T> constructor : constructors) {
// adapt for var args
if (constructor.getParameterCount() != argumentTypes.length)
continue;
Class<?>[] formalTypes = constructor.getParameterTypes();
for (int i = 0; i < formalTypes.length; i++) {
if (!formalTypes[i].isAssignableFrom(argumentTypes[i]))
continue;
}
return constructor;
}
return null; // or throw
}
答案 2 :(得分:0)
这是Sotirios的高级版本&#39;代码。
我添加了一个getDeclaredConstructors()
方法,您可以直接传递类和args并返回新实例。我添加了空检查和正确的错误处理+很好的错误消息。
我将getConstructors()
更改为/**
* Creates a new instance for clazz using it's constructor matching the given args.
* As opposed to the <code>clazz.getConstructor(...).newInstance(args)</code> method
* this method considers also constructors with matching super-type parameters
* (as we know it from normal method or constructor invocations).
*
* @param clazz
* @param args
* @return
* @throws InstantiationException
* @throws IllegalAccessException
* @throws IllegalArgumentException
* @throws InvocationTargetException
* @throws NoSuchMethodException
*/
public static <T> T newInstance(Class<? extends T> clazz, Object... args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException
{
return findConstructor(clazz, args).newInstance(args);
}
/**
* Adopted from http://stackoverflow.com/a/29312271/1520422
*
* @param clazz
* @param args
* @return
* @throws NoSuchMethodException
*/
@SuppressWarnings("unchecked")
public static <T> Constructor<T> findConstructor(Class<T> clazz, Object... args) throws NoSuchMethodException
{
Constructor<T> matchingConstructor = null;
Constructor<T>[] constructors = (Constructor<T>[]) clazz.getConstructors();
for (Constructor<T> constructor : constructors)
{
if (constructor.getParameterCount() != args.length)
{
continue;
}
Class<?>[] formalTypes = constructor.getParameterTypes();
for (int i = 0; i < formalTypes.length; i++)
{
if (!formalTypes[i].isInstance(args[i]))
{
continue;
}
}
if (matchingConstructor != null) // already found one ... so there is more than one ...
{
throw new NoSuchMethodException("Multiple constructors found for: " + printArgs(clazz, args) + " --> " + matchingConstructor + " --> " + constructor);
}
matchingConstructor = constructor;
}
if (matchingConstructor == null)
{
throw new NoSuchMethodException("No constructor found for: " + printArgs(clazz, args));
}
return matchingConstructor;
}
private static String printArgs(Class<?> clazz, Object... args)
{
StringBuilder msg = new StringBuilder();
msg.append("new ");
msg.append(clazz.getName());
msg.append("(");
for (int i = 0; i < args.length; i++)
{
if (i > 0)
{
msg.append(", ");
}
msg.append(args[i] == null ? "null" : args[i].getClass().getName());
}
msg.append(")");
return msg.toString();
}
,因此我们只考虑实际可以调用的公共构造函数。此更改取决于要求,并且可能必须在某些情况下还原。
def __str__(self):
space_sym = " "*10 # hardcode magic-number of indent spaces
return 'more spaces {}{}{}!'.format(self.foo, space_sym, self.bar)