Diskussion:Alle Überschreibungen einer Methode ermitteln

Aus Eclipse
Version vom 17. Mai 2009, 20:43 Uhr von Mueller (Diskussion | Beiträge)

(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)
Wechseln zu: Navigation, Suche

Begriffe: überschreiben versus implementieren

Die Erinnerung an Javas Nomenklatur für überschreiben am Anfang des Artikels soll Missverständnisse vermeiden. Die Begriffe überschreiben und implementieren werden gelegentlich lax gebraucht und haben in verschiedenen Programmiersprachen zudem unterschiedliche Bedeutung. So können auch Experten unsicher sein, wie die kleinen grünen gefüllten oder nicht gefüllten Dreiecke in Eclipse zu interpretieren sind, die überschreibende bzw. implementierende Methoden kennzeichnen (siehe Eclipse Bug 90660).

Java 1.5 scheint den Begriff überschreiben selbst nicht ganz konsistent zu verwenden. Die an sich sehr hilfreiche Annotation @Override hat eine etwas andere Bedeutung als die im Artikel zitierte Definition. In JLS3, § 9.6.1.4 heißt es: "Überschreibt eine mit @Override annotierte Methode die Methode eines Superinterfaces, nicht aber die einer (abstrakten) Methode einer Superklasse, verursacht die Annotation @Override einen Compilefehler" . @Override darf also nicht alle Überschreibungen kennzeichen. Diese strenge Regel ist für Java 1.6 wieder aufgeweicht. Das kann jedoch die ursprüngliche Intention der Annotation verwischen, versehentliche Neudefinitionen von Methoden zu verhindern.

Mueller


Code-Duplizierung versus Nutzung des internen API

Die hier vorgestellte Methode allOverriddenMethods(IMethodBinding) ruft anders als ihr Original aus dem AMM-Projekt eine Methode des internen JDT API auf (getAllSuperTypes(..)). Letztere kommt in den Eclipse JDT fast identisch gleich zwei mal vor, was das Problem der Wiedernutzung durch copy-and-paste illustriert. Folgender Quelltext stammt, hier etwas modernisiert, aus der Klasse org.eclipse.jdt.internal.corext.dom.Bindings:

    /**
     * Returns all super types (classes and interfaces) for the given type.
     *
     * Copyright (c) 2000, 2007 IBM Corporation and others. [...]
     */
    private static Set<ITypeBinding> allSupertypes(ITypeBinding type) {
        Set<ITypeBinding> result= new HashSet<ITypeBinding>();
        collectSuperTypes(result, type);
        result.remove(type);
        return result;
    }

    private static void collectSuperTypes(Set<ITypeBinding> supertypes,
            ITypeBinding type) {        // collecting parameter: supertypes

        if ( supertypes.add(type) ) {
            for ( ITypeBinding each : type.getInterfaces() ) {
                collectSuperTypes(supertypes, each);
            }
            
            ITypeBinding superClass= type.getSuperclass();
            if ( superClass != null ) {
                collectSuperTypes(supertypes, superClass);
            }
        }
    }

Es scheint wenig sinnvoll, diesen Code noch einmal zu duplizieren. Allerdings machen wir uns mit dem Aufruf der in Bindings implementierten Methode von der internen API der Eclipse JDT abhängig.

Mueller


AST/Bindings versus Java-Model versus Java-Search-Engine

Die angegebene Lösung des Problems, alle überschriebenen und überschreibenden Methoden zu finden, basiert auf den Bindings des Abstract Syntax Tree (AST). Dieser Ansatz scheint aus folgenden Gründen attraktiv:

  • Refaktorisierungswerkzeuge arbeiten meist bereits mit dem Modell des AST und seiner Bindings.
  • Nur das API des AST stellt die hier sehr praktische Instanz-Methode IMethodBinding#overrides(IMethodBinding) bereit.

Ein AST, insbesondere mit aufgelösten Bindings, stellt jedoch vergleichsweise hohe Anforderungen an Rechenzeit und Speicherplatz. Deshalb könnte es effizienter sein, das Problem mit Hilfe der Java-Search-Engine zunächst einzuschränken und dann auf der Ebene des Java-Models zu lösen (ein implementiertes Beispiel dafür habe ich noch nicht entdeckt).

Im zweiten Teil der Lösung verwenden wir Elemente des Java-Models um die Typhierarchie zu ermitteln. Eine ITypeHierarchy ermöglicht es, alle Subtypen (und Supertypen) ganz einfach zu ermitteln. Der Weg vom Binding des AST zum IJavaElement des Java-Models ist vergleichsweise günstig. Die hier jedoch ebenfalls benötigte Gegenrichtung vom Java-Model zum AST ist erheblich aufwändiger.

Um die IMethodBindings der entsprechenden Java-Model-Elemente zu ermitteln benutzen wir hier vorläufig internes, nicht zugängliches, weil nicht exportiertes API unseres Infer Type Projektes (Util_Conversion.getBinding(IMember)).

Mueller


Zerlegung der Aufgabe

Der vorgestellte zweite Teil der Lösung allein hätte ausgereicht, weil dort die komplette Typhierarchie schon vollständig berechnet wird. Möglicherweise wäre sie effizienter. Messungen stehen noch aus. Da der erste Teil nicht die Ebene vom AST zum Java-Model wechseln muss, scheint er einfacher nachvollziehbar. Das AMM-Projekt beschreitet diesen Weg. Und so gibt die Zerlegung der Problemlösung in zwei Teile die Gelegenheit, gleich zwei praktisch genutzte Ansätze zu demonstrieren.

Die hier diskutierten Lösungen gehen vom IBinding der gegebenen Methode aus. Wie man von einer Signatur und einem Empfängertyp zum IBinding der Methode kommt, werden wir an anderer Stelle dieses Wikis zeigen.

Mueller


Wo ist der Testcode?

Testcode fehlt leider noch, sorry.

Mueller