简体   繁体   English

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

[英]How to anonymously instantiate an abstract class stored in a Class object in Java?

If you have an abstract class you can instantiate it by deriving an concrete anonymous class. 如果你有一个抽象类,你可以通过派生一个具体的匿名类来实例化它。 This is an example: 这是一个例子:

abstract class A {
     abstract void hello ();
}

A say = new A () { void hello () { System.out.println ("hello"); } }

say.hello(); // -> hello

How to do the same if the class is stored in a Class object? 如果类存储在Class对象中,如何做同样的事情? Here is an example: 这是一个例子:

// -*- 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"
    }
}

The first instantiation (x) works fine but the second (y) fails because it tries to instantiate the abstract class directly without deriving a concrete class. 第一个实例化(x)工作正常但第二个(y)失败,因为它试图直接实例化抽象类而不派生具体类。 How can I do this in Java having only a Class object? 如何在只有Class对象的Java中执行此操作?

You may have a misunderstanding on how exactly anonymous classes work. 您可能对匿名类的工作原理存在误解。 An anonymous class is in fact a regular class just like any other and has its own class file. 一个匿名类实际上是一个普通类,就像任何其他类一样,并且有自己的类文件。 Java-the-language only provides some syntactic sugar over this and allows a less verbose syntax for something that you can exactly mimic by declaring a regular named top-level class in its own file. Java-the-language只为此提供了一些语法糖,并允许通过在自己的文件中声明常规命名的顶级类来完全模仿某些东西的详细语法。 This is why you will find the Reflection API useless for what you want to achieve. 这就是为什么您会发现Reflection API对您想要实现的目标毫无用处。 Basically, you want to dynamically create a class that doesn't have its class file. 基本上,您希望动态创建一个没有类文件的类。 For this you need a suitable library, such as javassist . 为此,您需要一个合适的库,例如javassist

If A would be an interface instead of an abstract class, you can do this with a dynamic proxy, but that doesn't work with an abstract class. 如果A是接口而不是抽象类,则可以使用动态代理执行此操作,但这不适用于抽象类。 Example of how this works with an interface: 如何使用接口的示例:

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();
    }
}

Abstract classes cannot be instantiated, so you actually need a new concrete class that extends the abstract class. 抽象类无法实例化,因此您实际上需要一个扩展抽象类的新具体类。 Classes are produced by java compiler from source code. 类由java编译器从源代码生成。 So write that source code and run java compiler. 所以编写源代码并运行java编译器。 This is not easy to do dynamically, as java compiler requires source code to reside in a file and puts compiled classes into file system too, but possible. 这不容易动态,因为java编译器需要源代码驻留在文件中并将编译后的类放入文件系统中,但这是可能的。 See how it is supposed to do at Generating Java classes dynamically . 了解如何动态生成Java类 Then you have to load compiled classes, which is another story. 然后你必须加载编译的类,这是另一个故事。

If you consider this as a "java limitation", probably you chosed wrong language for your task (or chosed a wrong task). 如果你认为这是一个“java限制”,你可能会为你的任务选择错误的语言(或选择错误的任务)。 Try dynamic languages based on JVM: Groovy, JRuby... there are plenty of them. 尝试基于JVM的动态语言:Groovy,JRuby ......有很多它们。

As Marko stated, an anonymous class is the same as any other at the file and byte code level. 正如Marko所说,匿名类与文件和字节码级别的任何其他类相同。 It is just language level syntactic sugar that makes small classes easy to write. 它只是语言级语法糖,使小班易于编写。

In your example, x.getClass() is not an abstract class. 在您的示例中, x.getClass()不是abstract类。 It is a subclass of AbstractClass , which by the definition of id() , is no longer abstract . 它是AbstractClass的子类,它通过id()的定义不再是abstract It probably has a name like anon$1 . 它的名字可能像anon$1

Of course, if it were abstract, you could not instantiate it. 当然,如果它是抽象的,你就无法实例化它。 This is exactly what you are trying to do in the assignment of y . 这正是你在y的赋值中想要做的。 Your reflection is equivalent to y = anon.AbstractClass(); 你的反射相当于y = anon.AbstractClass(); with overriding id() . 覆盖id() The reflection is erroring at runtime just as that statement would error at compile time. 反射在运行时出错,就像该语句在编译时会出错一样。

The following would likely (depending the existence of other anonymous classes, and their order) and run without error, but print "X": 以下可能(取决于其他匿名类的存在及其顺序)并且运行没有错误,但打印“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"

To that point... 到那时......

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

Nowhere in your code do you have a line that prints a "Y" character, so there should not be any reason to expect it. 在你的代码中没有任何地方你有一行打印“Y”字符,所以没有任何理由可以期待它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM