简体   繁体   中英

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? 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. How can I do this in Java having only a Class object?

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. This is why you will find the Reflection API useless for what you want to achieve. 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 .

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. 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. So write that source code and run java compiler. 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. See how it is supposed to do at Generating Java classes dynamically . 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). Try dynamic languages based on JVM: Groovy, JRuby... there are plenty of them.

As Marko stated, an anonymous class is the same as any other at the file and byte code level. It is just language level syntactic sugar that makes small classes easy to write.

In your example, x.getClass() is not an abstract class. It is a subclass of AbstractClass , which by the definition of id() , is no longer abstract . It probably has a name like 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 . Your reflection is equivalent to y = anon.AbstractClass(); with overriding 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":

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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