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

时间:2012-11-29 10:13:43

标签: java reflection

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

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> abstractclass 
        = (Class<AbstractClass>)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中执行此操作?

4 个答案:

答案 0 :(得分:5)

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

答案 1 :(得分:2)

如果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<A> cls = (Class<A>) 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();
    }
}

答案 2 :(得分:0)

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

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

答案 3 :(得分:0)

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

在您的示例中,x.getClass()不是abstract类。它是AbstractClass的子类,根据id()的定义,不再是abstract。它的名称可能类似于anon$1

当然,如果它是抽象的,你就无法实例化它。这正是您在分配y时要做的。您的反映相当于y = anon.AbstractClass();并覆盖id()。反射在运行时出错,就像该语句在编译时会出错一样。

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

Class<AbstractClass> abstractclass 
    = (Class<AbstractClass>)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”字符,因此没有任何理由可以期待它。