Java Reflection: Erstellen Sie eine implementierende Klasse

70
Class someInterface = Class.fromName("some.package.SomeInterface");

Wie erstelle ich jetzt eine neue Klasse, die implementiert someInterface?

Ich muss eine neue Klasse erstellen und sie an eine Funktion übergeben, SomeInterfacedie ein Argument benötigt.

Isaac Waller
quelle
Ich fürchte, es ist gar nicht so einfach, Klassen im laufenden Betrieb zu erstellen.
Michael Myers
1
@ MichaelMyers Es ist auch nicht so schwer, stackoverflow.com/a/9583681/632951
Pacerier

Antworten:

56

Es ist nicht allzu schwierig, etwas zu erstellen, das vorgibt, eine Schnittstelle im laufenden Betrieb zu implementieren. Sie können java.lang.reflect.Proxynach der Implementierung InvocationHandleralle Methodenaufrufe verarbeiten.

Natürlich können Sie mit einer Bibliothek wie BCEL tatsächlich eine echte Klasse generieren .

Wenn dies zu Testzwecken dient, sollten Sie sich spöttische Frameworks wie jMock und EasyMock ansehen .

Jon Skeet
quelle
2
Whoa, ordentlich! Ich frage mich, was noch im Paket java.lang.reflect enthalten ist, von dem ich nichts weiß.
Michael Myers
80

Leicht java.lang.reflect.Proxyzur Rettung!

Vollständiges Arbeitsbeispiel :

interface IRobot {

    String Name();

    String Name(String title);

    void Talk();

    void Talk(String stuff);

    void Talk(int stuff);

    void Talk(String stuff, int more_stuff);

    void Talk(int stuff, int more_stuff);

    void Talk(int stuff, String more_stuff);
}

public class ProxyTest {
    public static void main(String args[]) {
        IRobot robot = (IRobot) java.lang.reflect.Proxy.newProxyInstance(
                IRobot.class.getClassLoader(),
                new java.lang.Class[] { IRobot.class },
                new java.lang.reflect.InvocationHandler() {

            @Override
            public Object invoke(Object proxy, java.lang.reflect.Method method, Object[] args) throws java.lang.Throwable {
                String method_name = method.getName();
                Class<?>[] classes = method.getParameterTypes();

                if (method_name.equals("Name")) {
                    if (args == null) {
                        return "Mr IRobot";
                    } else {
                        return args[0] + " IRobot";
                    }
                } else if (method_name.equals("Talk")) {
                    switch (classes.length) {
                        case 0:
                            System.out.println("Hello");
                            break;
                        case 1:
                            if (classes[0] == int.class) {
                                System.out.println("Hi. Int: " + args[0]);
                            } else {
                                System.out.println("Hi. String: " + args[0]);
                            }
                            break;
                        case 2:
                            if (classes[0] == String.class) {
                                System.out.println("Hi. String: " + args[0] + ". Int: " + args[1]);
                            } else {
                                if (classes[1] == String.class) {
                                    System.out.println("Hi. int: " + args[0] + ". String: " + args[1]);
                                } else {
                                    System.out.println("Hi. int: " + args[0] + ". Int: " + args[1]);
                                }
                            }
                            break;
                    }
                }
                return null;
            }
        });

        System.out.println(robot.Name());
        System.out.println(robot.Name("Dr"));
        robot.Talk();
        robot.Talk("stuff");
        robot.Talk(100);
        robot.Talk("stuff", 200);
        robot.Talk(300, 400);
        robot.Talk(500, "stuff");
    }
}
Pacerier
quelle
Dies ist ein guter zusätzlicher Beitrag zu dieser Antwort: tutorials.jenkov.com/java-reflection/dynamic-proxies.html
jonashackt
3

Wenn Sie über Schnittstellen hinausgehen möchten, sollten Sie sich cglib und objenesis ansehen . Zusammen ermöglichen sie es Ihnen, einige ziemlich mächtige Dinge zu tun, eine abstrakte Klasse zu erweitern und sie zu instanziieren. ( jMock verwendet sie beispielsweise zu diesem Zweck.)

Wenn Sie sich an Schnittstellen halten möchten, tun Sie, was Jon Skeet gesagt hat :).

jqno
quelle
-4

Tatsächlich müssen Sie den Klassennamen in der Methode Class.fromName () verwenden und in Ihren Schnittstellentyp umwandeln. Überprüfen Sie, ob das folgende Beispiel hilft.

public class Main {

    public static void main(String[] args) throws Exception {
        Car ferrari = (Car) Class.forName("Mercedez").newInstance();
        System.out.println(ferrari.getName());
    }
}

interface Car {
    String getName();
}

class Mercedez implements Car {

    @Override
    public String getName() {
        return "Mercedez";
    }

}

class Ferrari implements Car {

    @Override
    public String getName() {
        return "Ferrari";
    }

}
Nandokakimoto
quelle
2
Dies verfehlt den Punkt der Frage. Die Frage des OP ist etwas unklar, aber sie suchen nach einer Möglichkeit, eine Klasse zur Laufzeit zu implementieren. Erstellen Sie nicht einfach ein Objekt einer unbekannten Klasse, sondern erstellen Sie effektiv eine neue Klasse.
Philip Couling
Es ist nicht einmal in der Nähe des Themas
Herunterfahren -h jetzt