it-swarm.dev

Was ist falsch an Javas Generika?

Ich habe auf dieser Site mehrmals Beiträge gesehen, die die Implementierung von Generika durch Java entschlüsseln. Jetzt kann ich ehrlich sagen, dass ich keine Probleme mit der Verwendung hatte. Ich habe jedoch nicht versucht, selbst eine generische Klasse zu erstellen. Was sind Ihre Probleme mit der generischen Unterstützung von Java?

50
Michael K

Die generische Implementierung von Java verwendet Typ Erasure . Dies bedeutet, dass Ihre stark typisierten generischen Sammlungen zur Laufzeit tatsächlich vom Typ Object sind. Dies hat einige Leistungsaspekte, da primitive Typen beim Hinzufügen zu einer generischen Sammlung eingerahmt werden müssen. Natürlich überwiegen die Vorteile der Typkorrektheit zur Kompilierungszeit die allgemeine Albernheit der Typlöschung und den obsessiven Fokus auf Abwärtskompatibilität.

56
ChaosPandion

Beachten Sie, dass sich die bereits bereitgestellten Antworten auf die Kombination von Java-the-language, JVM und der Klassenbibliothek Java) konzentrieren.

An Javas Generika ist in Bezug auf Java-the-language nichts auszusetzen. Wie in C # vs Java Generics beschrieben, sind Javas Generics auf Sprachebene ziemlich gut1.

Suboptimal ist, dass die JVM Generika nicht direkt unterstützt, was mehrere wichtige Konsequenzen hat:

  • die reflexionsbezogenen Teile der Klassenbibliothek können nicht die vollständigen Typinformationen anzeigen, die in Java-the-language verfügbar waren
  • es gibt einige Leistungseinbußen

Ich nehme an, die Unterscheidung, die ich mache, ist pedantisch, da Java-the-language allgegenwärtig mit JVM als Ziel und Java Class Library als Kernbibliothek kompiliert wird.

1 Mit der möglichen Ausnahme von Platzhaltern, von denen angenommen wird, dass sie die Typinferenz im allgemeinen Fall unentscheidbar machen. Dies ist ein wesentlicher Unterschied zwischen C # und Java Generika, die überhaupt nicht oft erwähnt werden. Danke, Antimon.

26
Roman Starkov

Die übliche Kritik ist das Fehlen einer Verdinglichung. Das heißt, Objekte zur Laufzeit enthalten keine Informationen zu ihren generischen Argumenten (obwohl die Informationen in Feldern, Methoden, Konstruktoren sowie erweiterten Klassen und Schnittstellen noch vorhanden sind). Dies bedeutet, dass Sie beispielsweise ArrayList<String> In List<File> Umwandeln können. Der Compiler gibt Ihnen Warnungen aus, aber er warnt Sie auch, wenn Sie einen ArrayList<String> Einer Object Referenz zuweisen und ihn dann in List<String> Umwandeln. Die Vorteile sind, dass Sie mit Generika wahrscheinlich nicht das Casting durchführen sollten, die Leistung ohne unnötige Daten und natürlich die Abwärtskompatibilität besser ist.

Einige Leute beschweren sich, dass Sie nicht aufgrund generischer Argumente (void fn(Set<String>) und void fn(Set<File>)) überladen können. Sie müssen stattdessen bessere Methodennamen verwenden. Beachten Sie, dass für diese Überladung keine erneute Überprüfung erforderlich ist, da die Überladung ein statisches Problem bei der Kompilierung darstellt.

Primitive Typen funktionieren nicht mit Generika.

Platzhalter und Grenzen sind ziemlich kompliziert. Sie sind sehr nützlich. Wenn Java Unveränderlichkeit und Tell-Don't-Ask-Schnittstellen bevorzugt, sind deklarationsseitige Generika anstelle von nutzungsseitigen Generika besser geeignet.

13

Java-Generika sind zum Kotzen, weil Sie Folgendes nicht tun können:

public class AsyncAdapter<Parser,Adapter> extends AsyncTask<String,Integer,Adapter> {
    proptected Adapter doInBackground(String... keywords) {
      Parser p = new Parser(keywords[0]); // this is an error
      /* some more stuff I was hoping for but couldn't do because
         the compiler wouldn't let me
      */
    }
}

Sie müssten nicht jede Klasse im obigen Code abschlachten, damit sie ordnungsgemäß funktioniert, wenn Generika tatsächlich generische Klassenparameter wären.

10
davidk01
  • compiler-Warnungen für fehlende generische Parameter, die keinen Zweck erfüllen, machen die Sprache sinnlos ausführlich, z. B. public String getName(Class<?> klazz){ return klazz.getName();}

  • Generics spiele nicht Nizza mit Arrays

  • Verlorene Typinformationen machen die Reflexion zu einem Durcheinander von Guss und Klebeband.

5
Steve B.

Ich denke, andere Antworten haben dies bis zu einem gewissen Grad gesagt, aber nicht sehr klar. Eines der Probleme mit Generika ist der Verlust der generischen Typen während des Reflexionsprozesses. Also zum Beispiel:

List<String> arr = new ArrayList<String>();
assertTrue( ArrayList.class, arr.getClass() );
TypeVarible[] types = arr.getClass().getTypedVariables();

Leider können die zurückgegebenen Typen nicht sagen, dass die generischen Typen von arr String sind. Es ist ein subtiler Unterschied, aber es ist wichtig. Da arr zur Laufzeit erstellt wird, werden die generischen Typen zur Laufzeit gelöscht, sodass Sie dies nicht herausfinden können. Wie einige sagten ArrayList<Integer> sieht genauso aus wie ArrayList<String> vom Standpunkt der Reflexion.

Dies mag für den Benutzer von Generics nicht von Bedeutung sein, aber nehmen wir an, wir wollten ein ausgefallenes Framework erstellen, das mithilfe von Reflexion ausgefallene Dinge darüber herausfindet, wie der Benutzer die konkreten generischen Typen einer Instanz deklariert.

Factory<MySpecialObject> factory = new Factory<MySpecialObject>();
MySpecialObject obj = factory.create();

Angenommen, wir wollten, dass eine generische Factory eine Instanz von MySpecialObject erstellt, da dies der konkrete generische Typ ist, den wir für diese Instanz deklariert haben. Die Factory-Klasse kann sich nicht selbst abfragen, um den für diese Instanz deklarierten konkreten Typ herauszufinden, da Java sie gelöscht hat).

In den Generika von .Net können Sie dies tun, da das Objekt zur Laufzeit die generischen Typen kennt, da der Compiler sie in die Binärdatei übernommen hat. Mit Löschungen Java kann dies nicht.

5
chubbsondubs

Ich könnte ein paar gute Dinge über Generika sagen, aber das war nicht die Frage. Ich könnte mich beschweren, dass sie zur Laufzeit nicht verfügbar sind und nicht mit Arrays funktionieren, aber das wurde bereits erwähnt.

Ein großer psychologischer Ärger: Ich gerate gelegentlich in Situationen, in denen Generika nicht zum Funktionieren gebracht werden können. (Arrays sind das einfachste Beispiel.) Und ich kann nicht herausfinden, ob Generika den Job nicht machen können oder ob ich einfach nur dumm bin. Ich hasse das. So etwas wie Generika sollten immer funktionieren. Jedes Mal, wenn ich mit Java-the-language nicht tun kann, was ich will, weiß ich , dass ich das Problem bin, und ich weiß, ob ich es einfach behalte Schieben, ich werde irgendwann dort ankommen. Wenn ich mit Generika zu hartnäckig werde, kann ich viel Zeit verschwenden.

Aber das eigentliche Problem ist, dass Generika zu viel Komplexität für zu wenig Gewinn hinzufügen. In den einfachsten Fällen kann es mich davon abhalten, ein Apple zu einer Liste mit Autos hinzuzufügen. Gut. Aber ohne Generika würde dieser Fehler eine ClassCastException sehr schnell zur Laufzeit mit wenig Zeitverschwendung auslösen. Wenn ich Fügen Sie ein Auto mit einem Kindersitz mit einem Kind hinzu. Benötige ich eine Warnung zur Kompilierungszeit, dass die Liste nur für Autos mit Kindersitzen gilt, die Babyschimpansen enthalten? Eine Liste einfacher Objektinstanzen scheint eine gute Idee zu sein.

Generierter Code kann viele Wörter und Zeichen enthalten, viel zusätzlichen Platz beanspruchen und das Lesen viel länger dauern. Ich kann viel Zeit damit verbringen, den ganzen zusätzlichen Code richtig zum Laufen zu bringen. Wenn es so ist, fühle ich mich wahnsinnig schlau. Ich habe auch mehrere Stunden oder mehr meiner eigenen Zeit verschwendet und ich muss mich fragen, ob irgendjemand anders jemals in der Lage sein wird, den Code herauszufinden. Ich möchte es zur Wartung an Leute weitergeben können, die entweder weniger klug als ich sind oder weniger Zeit zum Verschwenden haben.

Auf der anderen Seite (ich bin dort ein bisschen aufgewickelt und habe das Bedürfnis, ein Gleichgewicht zu finden), ist es schön, wenn man einfache Sammlungen und Karten verwendet habe beim Schreiben hinzugefügt, gesetzt und überprüft, und es fügt dem Code normalerweise nicht viel Komplexität hinzu ( if jemand else schreibt die Sammlung oder Karte) . Und Java ist hier besser als C #. Es scheint, dass keine der C # -Sammlungen, die ich verwenden möchte, jemals mit Generika umgehen kann. (Ich gebe zu, ich habe einen seltsamen Geschmack in Sammlungen.)

1
RalphChapin