Bestimmen Sie, ob eine Klasse eine Schnittstelle in Java implementiert

89

Ich habe ein ClassObjekt. Ich möchte feststellen, ob der Typ, den das ClassObjekt darstellt, eine bestimmte Schnittstelle implementiert. Ich habe mich gefragt, wie dies erreicht werden kann.

Ich habe den folgenden Code. Grundsätzlich wird ein Array aller Klassen in einem angegebenen Paket abgerufen. Ich möchte dann das Array durchgehen und die Klassenobjekte hinzufügen, die eine Schnittstelle zu meiner Karte implementieren. Problem ist, dass isInstance()ein Objekt als Parameter verwendet wird. Ich kann keine Schnittstelle instanziieren. Ich bin damit irgendwie ratlos. Irgendwelche Ideen?

Class[] classes = ClassUtils.getClasses(handlersPackage);
for(Class clazz : classes)
{
    if(clazz.isInstance(/*Some object*/)) //Need something in this if statement
    {
        retVal.put(clazz.getSimpleName(), clazz);
    }
}
user489041
quelle

Antworten:

212

Sie sollten verwenden isAssignableFrom:

if (YourInterface.class.isAssignableFrom(clazz)) {
    ...
}
Flavio
quelle
Dies funktioniert, wenn das Projekt dasselbe ist. Wenn Sie jedoch den Schnittstellencode 1: 1 kopieren, ein neues Projekt und ein neues JAR erstellen und dann versuchen, dieses JAR als Plugin zu laden, gibt der Aufruf false zurück. Vergleichen mit Namen dann "funktioniert", wie Roddy geschrieben hat. Ich habe jedoch keine Ahnung, wie ich dies überprüfen soll, wie Java schließlich die Kompatibilität überprüft. Mit Namen ist ein schmutziger Ansatz. Ihr ist natürlich in Ordnung, wenn das Projekt das gleiche ist. ................ Vielleicht mache ich es falsch: Ich erstelle eine URLClassLoader-Instanz für die Plugin-Datei und lade sie so. Vielleicht sollte ich einen anderen Klassenlader ausprobieren.
Dreamspace Präsident
4
Sie haben Probleme beim Laden von Klassen. Wenn Sie dieselbe Klasse zweimal mit verschiedenen Klassenladeprogrammen laden, sind die beiden ClassInstanzen nicht kompatibel. Sie könnten Fehler wie java.lang.ClassCastException: com.my.CustomClass cannot be cast to com.my.CustomClassoder etwas ähnlich Unerklärliches sehen.
Flavio
Ich habe inzwischen verschiedene Ansätze ausprobiert, und am Ende stellte sich heraus, dass das Hauptproblem, das ich hatte, folgendes war: Während die Schnittstelle in meinem Plugin und im Hauptprojekt identisch war, befanden sie sich nicht an derselben Stelle, also der Namespace / die Adresse war eine andere. Übrigens benutze ich jetzt: myClassLoader = new URLClassLoader(new URL[] { candidateFile.toURI().toURL() }, LoadedPlugin.class.getClassLoader());und classToLoad = Class.forName("com.blablabla.plugin.Main", true, myClassLoader);und instance = (MyIntf) classToLoad.newInstance();funktioniert wie ein Zauber.
Dreamspace Präsident
17

Mit der folgenden Funktion können Sie alle implementierten Schnittstellen abrufen

Class[] intfs = clazz.getInterfaces();
Ankur
quelle
10

Sie können verwenden class.getInterfaces()und dann überprüfen, ob sich die Schnittstellenklasse dort befindet.

Class someInterface; // the interface you want to check for 
Class x; // 
Class[] interfaces = x.getInterfaces();

for (Class i : interfaces) {
    if (i.toString().equals(someInterface.toString()) {
        // if this is true, the class implements the interface you're looking for
    }
}
Roddy der gefrorenen Erbsen
quelle
Dieser Ansatz wird technisch funktionieren, aber der viel einfachere und sauberere Ansatz ist die Verwendung, isAssignableFromwie Flavio erwähnt.
JWJ
Ja, das stimmt, obwohl Ihre Antwort mehr als ein paar Mal positiv bewertet wurde und ich dachte, es wäre nützlich, einen Kontext hinzuzufügen. Während die Verwendung isAssignableFromwahrscheinlich vorzuziehen ist, kann es Fälle geben, in denen Sie die Liste der von einer Klasse implementierten Schnittstellen anhand der Namen scannen müssen.
JWJ
Dies funktioniert tatsächlich nicht. GetInterfaces () funktioniert nur, wenn die Klasse die Schnittstelle direkt implementiert. Wenn eine Superklasse die Schnittstelle implementiert oder eine Superschnittstelle sie erweitert, wird diese Schnittstelle von getInterfaces () nicht zurückgegeben. Sie müssen den Baum aller Superklassen und Schnittstellen durchlaufen, um alle von der Klasse implementierten Schnittstellen zu erhalten.
James Roper
Das war aber nicht die Frage.
Roddy der gefrorenen Erbsen
1

Sie können die Instanz auch so einstellen, dass ".class" hinzugefügt wird.

Class[] classes = ClassUtils.getClasses(handlersPackage);
for(Class clazz : classes)
{
    if(Interface.class.isAssignableFrom(clazz))
    {
        retVal.put(clazz.getSimpleName(), clazz);
    }
}
Manu Navarro
quelle
2
Wenn Sie sich diesen Ansatz ansehen, beachten Sie bitte die Antwort von Flavio. Beachten Sie, dass der Code in diesem Beispiel einige Dinge ausführt, die möglicherweise nicht sofort sinnvoll sind: Er ClassUtilsist nicht Teil von Java (in Guava oder Spring und anderen Frameworks). Der oben verwendete Begriff Interfacebezieht sich auf eine bestimmte getestete Schnittstelle ( dh es ist in diesem Zusammenhang kein Java-Schlüsselwort), und der Zweck von retValwird nirgendwo erklärt oder erwähnt.
JWJ