Spezielle Verwendung von Java Generics: 'hack' oder 'nice produktivity boost'?

8

Wir haben einige Definitionen und die Verwendung von Generika in unserem Code vereinfacht. Jetzt haben wir einen interessanten Fall, nehmen Sie dieses Beispiel:

%Vor%

Sehen Sie sich nun die Methode 'doSeomthingWeird () in MyWeirdClass an: Dieser Code wird mithilfe des Eclipse-JDT-Compilers korrekt kompiliert, wird jedoch bei Verwendung des Oracle-Compilers fehlschlagen. Da der JDT in der Lage ist, einen funktionierenden Bytecode zu erzeugen, bedeutet dies, dass dies auf JVM-Ebene ein gültiger Code ist, und es ist "nur" der Oracle-Compiler, der es nicht erlaubt, solche schmutzigen (!?) Zeug zu kompilieren. Wir verstehen, dass der Compiler von Oracle den Aufruf 'T obj = getMyClass ()' nicht akzeptiert. da T kein wirklich existierender Typ ist. Da wir jedoch wissen, dass das zurückgegebene Objekt A und B implementiert, warum nicht zulassen? (Der JDT-Compiler und die JVM tun das). Beachten Sie außerdem, dass der Generics-Code nur intern in privaten Methoden verwendet wird. Daher möchten wir sie nicht auf Klassenebene verfügbar machen und externen Code mit generischen Definitionen belasten, an denen wir nicht interessiert sind (außerhalb der Klasse).

Die Schulbuchlösung wird sein, um eine Schnittstelle zu schaffen AB erweitert A, B jedoch, da wir eine größere Anzahl von Schnittstellen haben, die in verschiedenen Kombinationen verwendet werden und von verschiedenen Modulen kommen, was gemeinsame Schnittstellen für alle Kombinationen signifikant erhöht Anzahl der "Dummy" -Schnittstellen und schließlich den Code weniger lesbar. In der Theorie würde es bis zu N-Permutationen von verschiedenen Wrapper-Schnittstellen erfordern, um alle Fälle abzudecken. Die "business-oriented-engineer" (andere nennen es die "lazy engineer") Lösung wäre, den Code auf diese Weise zu belassen und nur JDT zum Kompilieren des Codes zu verwenden. Edit: Es ist ein Fehler in Oracles Javac 6 und funktioniert auch auf Oracle's Javac 7

problemlos

Was meinst du? Gibt es irgendwelche versteckten Gefahren durch die Annahme dieser "Strategie"?

Hinzufügung, um Diskussionen über (für mich) nicht relevante Punkte zu vermeiden: Ich frage nicht, warum der obige Code nicht mit dem Compiler von Oracle kompiliert wird. Ich kenne den Grund und möchte diese Art von Code nicht ohne einen guten Grund ändern, wenn er bei Verwendung eines anderen Compilers perfekt funktioniert. Bitte konzentrieren Sie sich auf die Definition und Verwendung (ohne Angabe eines bestimmten Typs) der Methode 'doSomethingWeird ()'. Gibt es einen guten Grund, warum wir nicht nur den JDT-Compiler verwenden sollten, der es erlaubt, diesen Code zu schreiben und zu kompilieren und die Kompilierung mit dem Compiler von Oracle zu stoppen, der den obigen Code nicht akzeptiert? (Danke für die Eingabe)

Bearbeiten : Der obige Code wird auf Oracle Javac 7, aber nicht auf Javac 6 korrekt kompiliert. Es ist ein Javac 6 Bug. Das bedeutet also, dass in unserem Code nichts falsch ist und wir es beibehalten können. Die Frage wird beantwortet, und ich werde es nach Ablauf der zwei Tage auf meine eigene Antwort als solche markieren. Danke an alle für das konstruktive Feedback.

    
Danilo Tommasina 21.02.2012, 07:58
quelle

7 Antworten

0

So haben die Jungs von OpenJDK auf meine Frage geantwortet:

  

Diese Fehler werden durch die Tatsache verursacht, dass der JDK 6-Compiler dies nicht tut   Typinferenz korrekt implementieren. Es wurde viel Mühe investiert   JDK 7 Compiler, um alle diese Probleme loszuwerden (Ihr Programm   kompiliert in JDK 7). Einige dieser Schlußfolgerungen sind jedoch verbesserungswürdig   Quellkompatible Änderungen erfordern, weshalb wir keinen Backport durchführen können   Diese Korrekturen in der JDK 6-Version.

Das heißt also für uns: Es ist absolut nichts falsch an unserem Code und wird offiziell auch von Oracle unterstützt. Wir können auch bei dieser Art von Code bleiben und Javac 7 mit target = 1.6 für unsere Maven Builds verwenden, während die Entwicklung in Eclipse garantiert, dass wir keine Java 7 APIs verwenden: D yaaahyyy ​​!!!

    
Danilo Tommasina 21.02.2012, 13:54
quelle
10

In Java können Sie generische Methoden verwenden, wenn der generische Typ in Parameter oder Rückgabetyp der Methodensignatur verwendet wird. In Ihrer Beispiel-generischen doSomethingWeird -Methode, aber nie in der Methodensignatur verwendet. siehe folgendes Beispiel:

%Vor%

Dieser Code funktioniert gut.

JLS (Java Language Specification) sagt in generische Methode Teil:

%Vor%

Nach diesem Zitat, wenn Sie T in doSomethingWeird Methodensignatur nicht verwenden, Was geben Sie den Rohtyp von T in der Aufrufzeit an (in entryPoint method)?

    
Sam 21.02.2012 08:19
quelle
0

Ich habe den Code nicht überprüft (kompiliere mit beiden Compilern). Es gibt eine Menge seltsamer Dinge in der Sprachspezifikation auf einer noch grundlegenderen Ebene (überprüfen Sie die Array-Deklaration ...). Ich glaube jedoch, dass das obige Design wenig "überentwickelt" ist, und wenn ich die Notwendigkeit richtig übersetze, kann die erforderliche Funktionalität mit Factory Muster oder wenn Sie ein IoC-Framework (Spring?) Verwenden, dann kann die Methode der Nachschlagetechnik die Magie für Sie tun. Ich denke, der Code wird intuitiver und leichter zu lesen und zu pflegen sein.

    
aviad 21.02.2012 08:25
quelle
0

Ich denke, die Ursache ist anders. Das ist nicht wahr, dass der Typ T in Zeile "T obj = getMyClass ();" ist unbekannt - aufgrund der Definition "T erweitert A & amp; B" ist seine Löschung A . Dies nennt man multiple bounds und es gilt: "Wenn eine mehrfache Grenze verwendet wird, wird der erste Typ, der in der Grenze erwähnt wird, als das Löschen der Typvariablen verwendet."

    
Rostislav Matl 21.02.2012 08:58
quelle
0

Basierend auf der Antwort von @ MJM empfehle ich Ihnen, den Code wie folgt zu aktualisieren. Dann wird sich Ihr Code nicht auf den JVM-Typ infer stützen.

%Vor%     
Kleenestar 21.02.2012 09:04
quelle
0

Ich würde wieder gehen, um das zu erstellen, was Sie eine "Dummy" -Schnittstelle AB genannt haben.

Erstens finde ich es überhaupt nicht dumm. Es gibt zwei Klassen mit den gleichen allgemeinen Methodendefinitionen und an einer Stelle müssen Sie eine davon verwenden, unabhängig davon, welche es tatsächlich ist. Das ist die genaue Verwendung der Erbschaft. Die Schnittstelle AB passt also perfekt hier. Und Generika sind hier die falsche Lösung. Generika waren niemals dazu gedacht, Vererbung zu implementieren.

Zweitens werden durch das Definieren der Schnittstelle alle Generics-Elemente in Ihrem Code entfernt und die Lesbarkeit verbessert. Tatsächlich macht das Hinzufügen einer Schnittstelle (oder Klasse) Ihren Code niemals weniger lesbar. Andernfalls wäre es besser, den gesamten Code in eine einzige Klasse zu legen.

    
adranale 21.02.2012 14:19
quelle
0

Ihr Ansatz ist fraglich, weil die ungeprüften Umwandlungen die Sicherheit des Laufzeittyps opfern. Betrachten Sie dieses Beispiel:

%Vor%

(Sie müssen das Programm möglicherweise mehrmals ausführen, um zu sehen, was den Wartungsprogrammierer verwirrt hat.)

Ja, in diesem einfachen Programm ist der Fehler ziemlich offensichtlich, aber was ist, wenn das Objekt um die Hälfte Ihres Programms herumgereicht wird, bis seine Schnittstelle verpasst wird? Wie würdest du herausfinden, woher das schlechte Objekt kam?

Wenn Sie das nicht überzeugte, was ist damit?

%Vor%

Ist das Eliminieren paarer Deklarationen wirklich das wert?

    
meriton 25.02.2012 00:06
quelle

Tags und Links