简体   繁体   中英

Java reflection get anonymous class' origin interface

I have the following HashMap. Its purpose is to be used to invoke certain methods of classes extending JPanel. The method being the value in the HashMap and the parameter passed being the key.

HashMap<Object, String> methodMapper = new HashMap<>();

projectMethodMapper.put("Some title", "setInfoTitle");

projectMethodMapper.put(new ActionListener() {

    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("working");
    }
}, "attachListener");

This particular HashMap is to be used for a class called InfoPanel and it has the following methods.

public void attachListener(ActionListener al) {
    this.button.addActionListener(al);
}

public void setInfoTitle(String name) {
    ((TitledBorder) this.getBorder()).setTitle(name);
}

Later on I iterate through the keys of the HashMap.

InfoPanel infoPanel = new InfoPanel();

for (Object key : keys) {
    Method method = InfoPanel.class.getDeclaredMethod(methodMapper.get(key), key.getClass());

    method.invoke(infoPanel, key));
}

As you can imagine, there is no problem when the key is a String object, the problem comes when getDeclaredMethod searches for a method attachListener with parameter of type MainPanel$1 as .getClass returns MainPanel$1 because it's an anonymous class created on the fly. My question is - how do I find what interface is used for the creation of the anonymous class used for the object instantiation?

A simple answer to your question

how do I find what interface is used for the creation of the anonymous class used for the object instantiation?

is in the code snippet below. But be aware, that your approach can be very problematic, and you should probably attempt to refactor it if at all possible.

public class Main {
    private static Class<?> getDeclaredInterface(Object object) {
        if (object == null) {
            throw new NullPointerException();
        }
        Class<?> objectClass = object.getClass();
        Class<?>[] implementedInterfaces = objectClass.getInterfaces();
        if (implementedInterfaces.length == 0) {
            throw new IllegalArgumentException(objectClass.getSimpleName() + " implements no interfaces.");
        }
        if (implementedInterfaces.length > 1) {
            throw new IllegalArgumentException(objectClass.getSimpleName() + " implements multiple interfaces.");
        }
        return implementedInterfaces[0];
    }

    public static void main(String... args) {
        Object object = (Closeable) () -> System.out.println("No-op.");
        System.out.println(getDeclaredInterface(object)); // prints 'interface java.io.Closeable'
    }
}

You don't need to use reflection for this. You can use a List<Cosumer<InfoPanel>> , which is bascially a list of functors that each take an InfoPanel as argument:

List<Consumer<InfoPanel>> commandList = new ArrayList<>();

commandList.add(panel -> 
    panel.attachListener(e -> System.out.println("working")) // using lambda for brievety
);

commandList.add(panel -> panel.setInfoTitle("Some Title"));

Then later on, when you create the InfoPanel :

InfoPanel infoPanel = new InfoPanel();

commandList.forEach(command -> command.accept(infoPanel));

Of course, this leverages Java8, but the same thing is possible with Java7 if needed.


If you insist on using reflection, you could create a datatype that stores the expected parameter types, as well as the method name and arguments:

class Signature {
    private final String name;
    private final Class<?>[] parameters;
    private final Object[] arguments;

    ...
}

Have a list of that:

List<Signature> signatures = ...;

signatures.add(new Signature("attachListener",
    new Class<?>[] { ActionListener.class },
    new Object[] { e -> System.out.println("working") }));

Then later on, use that parameter list for the lookup:

InfoPanel infoPanel = new InfoPanel();

for(Signature s : signatures) {
    Method m = InfoPanel.class.getDeclaredMethods(s.getName(), s.getParameters());

    m.invoke(infoPanel, s.getArguments());
}

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