如何匿名实例化存储在Java中的Class对象中的抽象类?

如果你有一个抽象类,你可以通过派生一个具体的匿名类来实例化它。 这是一个例子:

abstract class A { abstract void hello (); } A say = new A () { void hello () { System.out.println ("hello"); } } say.hello(); // -> hello 

如果类存储在Class对象中,如何做同样的事情? 这是一个例子:

 // -*- compile-command: "javac anon.java && java anon"; -*- class anon { anon () throws Exception {} abstract class AbstractClass { AbstractClass () throws Exception {} abstract void id (); } AbstractClass x = new AbstractClass () { void id () { System.out.println ("X"); } }; Class abstractclass = (Class)Class.forName ("anon$AbstractClass"); AbstractClass y = abstractclass.getConstructor().newInstance(); public static void main (String argv[]) throws Exception { anon main = new anon(); main.x.id(); // should print "X" main.y.id(); // should print "Y" } } 

第一个实例化(x)工作正常但第二个(y)失败,因为它试图直接实例化抽象类而不派生具体类。 如何在只有Class对象的Java中执行此操作?

您可能对匿名类的工作原理存在误解。 一个匿名类实际上是一个普通类,就像任何其他类一样,并且有自己的类文件。 Java-the-language只为此提供了一些语法糖,并且通过在自己的文件中声明常规命名的顶级类,可以为您完全模仿的内容提供更简洁的语法。 这就是为什么您会发现Reflection API对您想要实现的目标毫无用处。 基本上,您希望动态创建一个没有类文件的类。 为此,您需要一个合适的库,例如javassist

如果A是接口而不是抽象类,则可以使用动态代理执行此操作,但这不适用于抽象类。 如何使用接口的示例:

 import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; interface A { void hello(); } public class Example { public static void main(String[] args) throws Exception { @SuppressWarnings("unchecked") Class cls = (Class) Class.forName("A"); InvocationHandler handler = new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(method.getName()); return null; } }; A instance = (A) Proxy.newProxyInstance(cls.getClassLoader(), new Class[] { cls }, handler); instance.hello(); } } 

抽象类无法实例化,因此您实际上需要一个扩展抽象类的新具体类。 类由java编译器从源代码生成。 所以编写源代码并运行java编译器。 动态起来并不容易,因为java编译器需要源代码驻留在文件中并将编译后的类放入文件系统中,但这是可能的。 了解如何动态生成Java类 。 然后你必须加载编译的类,这是另一个故事。

如果你认为这是一个“java限制”,你可能会为你的任务选择错误的语言(或选择错误的任务)。 尝试基于JVM的动态语言:Groovy,JRuby ……有很多它们。

正如Marko所说,匿名类与文件和字节代码级别的任何其他类相同。 它只是语言级语法糖,使小class易于编写。

在您的示例中, x.getClass()不是abstract类。 它是AbstractClass的子类,通过id()的定义,它不再是abstract 。 它的名字可能像anon$1

当然,如果它是抽象的,你就无法实例化它。 这正是你在y的赋值中想要做的。 你的反思相当于y = anon.AbstractClass(); 覆盖id() 。 reflection在运行时是错误的,就像该语句在编译时会出错一样。

以下可能(取决于其他匿名类的存在及其顺序)并且运行没有错误,但打印“X”:

 Class abstractclass = (Class)Class.forName("anon$1"); // Note the different class name AbstractClass y = abstractclass.getConstructor().newInstance(); y.id(); // prints "X", not "Y" 

到那时……

 main.y.id(); // should print "Y" 

在你的代码中没有任何地方你有一行打印“Y”字符,所以没有任何理由可以期待它。