How to anonymously create an instance of an abstract class stored in a Class object in Java?

If you have an abstract class, you can create it by outputting a specific 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 a 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 instance (x) works fine, but the second (y) fails because it tries to instantiate an abstract class directly without calling a specific class. How can I do this in Java having only a class object?

+6
source share
4 answers

You may have a misunderstanding of how anonymous classes work. An anonymous class is actually a regular class, like any other, and has its own class file. The Java language only provides syntactic sugar and allows less detailed syntax for something that you can mimic exactly by declaring a regular top-level class in your 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 does not have its class file. For this you need a suitable library like javassist .

+5
source

If A is an interface instead of an abstract class, you can do this with a dynamic proxy, but this does not work with an abstract class. An example of how this works with the 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(); } } 
+2
source

Abstract classes cannot be created, so you really need a new concrete class that extends the abstract class. Classes are created by the java compiler from source code. So write this source code and run the java compiler. This is not easy to do dynamically, since the java compiler requires that the source code be in the file and put the compiled classes on the file system, but it is possible. See how it should be done in Dynamic Java Class Creation . Then you need to load the compiled classes, which is another story.

If you consider this a “java restriction”, you may have chosen the wrong language for your task (or chosen the wrong task). Try JVM-based dynamic languages: Groovy, JRuby ... there are many.

0
source

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

In your example, x.getClass() not an abstract class. This is a subclass of AbstractClass , which by definition id() no longer abstract . This name probably has the name anon$1 .

Of course, if it were abstract, you could not create it. This is exactly what you are trying to do when assigning y . Your reflection is equivalent to y = anon.AbstractClass(); with overriding id() . Reflection is a run-time error, as this statement will be a compile-time error.

It is possible that (depending on the availability of other anonymous classes and their order) and will run without errors, but type "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" 

In this moment...

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

Nowhere in your code do you have a line that prints the character "Y", so there should be no reason to expect this.

0
source

All Articles