在 Java 中使用反射来创建新实例,并将引用变量类型设置为新的实例类名?

2022-09-01 20:28:29

我查看的所有反射示例都显示了创建未知实现的新实例,并将该实现转换为其接口。这样做的问题是,现在您无法在实现类上调用任何新方法(仅重写),因为对象引用变量具有接口类型。这是我所拥有的:

Class c = null;
try {
    c = Class.forName("com.path.to.ImplementationType");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}
InterfaceType interfaceType = null;
try {
    interfaceType = (InterfaceType)c.newInstance();
} catch (InstantiationException e) {
    e.printStackTrace();
} catch (IllegalAccessException e) {
    e.printStackTrace();
}

如果我只有对“com.path.to.ImplementationType”的引用,并且我不知道该类型可能是什么(它来自配置文件),那么我如何使用类名将其转换为EnvimentType?这有可能吗?


答案 1

这句话似乎总结了你问题的症结所在:

这样做的问题是,现在您无法在实现类上调用任何新方法(仅重写),因为对象引用变量具有接口类型。

你完全陷入了当前的实现,因为你不仅要尝试强制转换,你还需要定义你想要在这个子类上调用的方法。我看到两个选项:

1. 如其他地方所述,您不能使用类名的 String 表示形式将反射的实例强制转换为已知类型。但是,您可以使用测试来确定您的类是否属于所需的类型,然后执行硬编码的强制转换:Stringequals()

try {
   String className = "com.path.to.ImplementationType";// really passed in from config
   Class c = Class.forName(className);
   InterfaceType interfaceType = (InterfaceType)c.newInstance();
   if (className.equals("com.path.to.ImplementationType") {
      ((ImplementationType)interfaceType).doSomethingOnlyICanDo();
   } 
} catch (Exception e) {
   e.printStackTrace();
}

这看起来很丑陋,它破坏了你拥有的不错的配置驱动过程。我不建议你这样做,这只是一个例子。

2.您拥有的另一种选择是将反射从仅/创建扩展到包含反射。如果可以从配置文件传入的字符串创建,则还可以从该配置文件传入方法名称,并通过反射从对象中获取其自身的实例。然后,您可以在 上调用 (http://java.sun.com/javase/6/docs/api/java/lang/reflect/Method.html#invoke(java.lang.Object, java.lang.Object...),传入您创建的类的实例。我认为这将帮助你得到你所追求的。ClassObjectMethodClassMethodClassinvokeMethod

下面是一些代码作为示例。请注意,我冒昧地对方法的参数进行了硬编码。您也可以在配置中指定它们,并且需要反映它们的类名来定义它们的obejcts和实例。Class

public class Foo {

    public void printAMessage() {
    System.out.println(toString()+":a message");
    }
    public void printAnotherMessage(String theString) {
        System.out.println(toString()+":another message:" + theString);
    }

    public static void main(String[] args) {
        Class c = null;
        try {
            c = Class.forName("Foo");
            Method method1 = c.getDeclaredMethod("printAMessage", new Class[]{});
            Method method2 = c.getDeclaredMethod("printAnotherMessage", new Class[]{String.class});
            Object o = c.newInstance();
            System.out.println("this is my instance:" + o.toString());
            method1.invoke(o);
            method2.invoke(o, "this is my message, from a config file, of course");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException nsme){
            nsme.printStackTrace();
        } catch (IllegalAccessException iae) {
            iae.printStackTrace();
        } catch (InstantiationException ie) {
            ie.printStackTrace();
        } catch (InvocationTargetException ite) {
            ite.printStackTrace();
        }
    }
}

和我的输出:

this is my instance:Foo@e0cf70
Foo@e0cf70:a message
Foo@e0cf70:another message:this is my message, from a config file, of course

答案 2
//====Single Class Reference used to retrieve object for fields and initial values. Performance enhancing only====          
Class<?>    reference           =   vector.get(0).getClass();
Object      obj                 =   reference.newInstance();
Field[]     objFields           =   obj.getClass().getFields();