6

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

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

4 回答 4

5

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

于 2012-11-29T10:49:23.070 回答
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();
    }
}
于 2012-11-29T10:34:44.940 回答
0

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

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

当然,如果它是抽象的,你就不能实例化它。这正是您在分配y. 您的反射相当于y = anon.AbstractClass();with overriding 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”字符,因此不应该有任何理由期待它。

于 2014-12-18T23:51:44.247 回答
0

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

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

于 2012-11-29T11:46:18.153 回答