简体   繁体   中英

How to pass an anonymous Class (not an instance) as a parameter to a method expecting type Class in Java

I have to call a method that takes a Class parameter:

public action(Class<? extends SomeInterface> classVariable);

I am able to do this:

action(new SomeInterface() { 
    // implement interface
  }.getClass());

But, can I get away without instantiating the object and calling the getClass() ?

How would you access an anonymous class without an instance? You would need a name to refer to it somehow, which it by definition does not have (sure it will have some compiler generated name at runtime, but to query that you need to first create it, and you can't create it without instantiating it).

So there is no way to achieve what you want.

(Long form of @PeterLawrey's answer )

The problem is that there is no syntax that defines an inner class without also instantiating it. My personal feeling is that this means it isn't properly supported by the language, and you shouldn't do it.

But technically, it's possible. Even if it is never executed, an anonymous inner class definition/instantiation will still be compiled and result in a class file. So, if you're willing to hack around and "guess" the name generated for the anonymous inner class, you can get its Class<?> instance with Class.forName .

import java.util.Arrays;

class X {
    @SuppressWarnings("unchecked")
    private Class<? extends Runnable> getAnon() {
        if (true) { // Circumvent dead code compiler error
            String innerName = getClass().getName() + "$1";
            System.out.println("Expect autogenerated inner class name to be " + innerName);
            try {
                return (Class<? extends Runnable>)Class.forName(innerName);
            } catch(ClassNotFoundException e) {
                throw new RuntimeException("CompileTimeException!", e);
            }
        } 
        // Here's our anonymous inner
        // And be careful:
        // if you stuff it into the else branch of the if above,
        // the class file won't get created
        new Runnable() {
            {
                System.out.println("Inner class instantiated!");
            }
            public void run() {
                System.out.println("My outer is \"" + name + "\".");
            }
        };
        // If you had multiple anonymous inner classes, the order would matter...
        return null;
    }

    final String name;

    X(String name) {
        this.name = name;
    }

    public static void main(final String... arguments) throws Exception {
        Class<? extends Runnable> clazz = new X("This won't be used").getAnon();
        System.out.println("Ctors: " + Arrays.toString(clazz.getDeclaredConstructors()));
        clazz.getDeclaredConstructor(X.class).newInstance(new X("magic")).run();
    }
}


This should print:

Expect autogenerated inner class name to be X$1
Ctors: [X$1(X)]
Inner class instantiated!
My outer is "magic".

Note how only one instance of the inner class is constructed, and the instance of X that getAnon is called on is not the one that the anonymous class is instantiated with.

So we can refute some other arguments that said this should be impossible:

  • You need a name to refer to the class (@PéterTörök): The Class instance stored in clazz is sufficient. (But we did have to cheat with the generated name X$1 .)
  • Inner classes need an outer class for instantiation (@PaŭloEbermann): All variables that the inner class "captures" become parameters to its generated constructor. this behaves the same way. (Try adding a parameter to getAnon and print it from run .)

You should be able to do the following:

public void action(Class<? extends SomeInterface> classVariable);

then call:

action(new SomeInterface() { // implement interface }.getClass());

Example code:

interface I {}

class A implements I
{    
}

public void action(Class<? extends I> c)
{
}


public void test()
{
   action(new I() {}.getClass());
}

Anonymous classes cannot be safely referred to by name, only but instance. You can predict the name and use that but its not elegant, or reliable.

private void neverCalled() {
    SomeInterface si = new SomeInterface() { 
        // implement interface
    };
}

Class anon = Class.forName(getClass()+"$1"); // hopefully its the first!

It is much simpler to give the class a name and use that. Many IDEs allow you to change one into the other very easily.

Maybe you're looking for this?

action(SomeInterface.class);

I think this could be what you're looking for, but this doesn't involve anonymous classes at all.

It might help to have more details on what the method action does with the class reference.

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