Ich dachte, ich würde diesen Softball jedem anbieten, der ihn aus dem Park schlagen möchte. Was sind Generika, was sind die Vorteile von Generika, warum, wo, wie soll ich sie verwenden? Bitte halten Sie es ziemlich einfach. Vielen Dank.
85
Antworten:
quelle
Ich hasse es wirklich, mich zu wiederholen. Ich hasse es, öfter dasselbe zu tippen, als ich muss. Ich mag es nicht, Dinge mehrmals mit kleinen Unterschieden zu wiederholen.
Anstatt zu erstellen:
Ich kann eine wiederverwendbare Klasse erstellen ... (für den Fall, dass Sie die Raw-Sammlung aus irgendeinem Grund nicht verwenden möchten)
Ich bin jetzt 3x effizienter und muss nur noch eine Kopie pflegen. Warum möchten Sie nicht weniger Code pflegen?
Dies gilt auch für Nicht-Sammlungsklassen wie a
Callable<T>
oder aReference<T>
, die mit anderen Klassen interagieren müssen. Wollen Sie wirklich erweiternCallable<T>
undFuture<T>
und alle anderen zugehörigen Klasse typsichere Versionen zu erstellen?Ich nicht.
quelle
Die Notwendigkeit einer Typumwandlung ist einer der größten Vorteile von Java-Generika , da sie zur Kompilierungszeit eine Typprüfung durchführen. Dies verringert die Möglichkeit von
ClassCastException
s, die zur Laufzeit ausgelöst werden können, und kann zu robusterem Code führen.Aber ich vermute, dass Sie sich dessen voll bewusst sind.
Anfangs habe ich auch den Nutzen von Generika nicht gesehen. Ich fing an, Java aus der 1.4-Syntax zu lernen (obwohl Java 5 zu diesem Zeitpunkt nicht verfügbar war), und als ich auf Generika stieß, hatte ich das Gefühl, dass es mehr Code zum Schreiben war, und ich verstand die Vorteile wirklich nicht.
Moderne IDEs erleichtern das Schreiben von Code mit Generika.
Die meisten modernen, anständigen IDEs sind intelligent genug, um das Schreiben von Code mit Generika zu unterstützen, insbesondere bei der Code-Vervollständigung.
Hier ist ein Beispiel für das Erstellen eines
Map<String, Integer>
mit aHashMap
. Der Code, den ich eingeben müsste, lautet:Und in der Tat ist das eine Menge zu tippen, nur um eine neue zu machen
HashMap
. In Wirklichkeit musste ich jedoch nur so viel tippen, bevor Eclipse wusste, was ich brauchte:Map<String, Integer> m = new Ha
Ctrl+SpaceZwar musste ich
HashMap
aus einer Liste von Kandidaten auswählen , aber im Grunde wusste die IDE, was sie hinzufügen sollte, einschließlich der generischen Typen. Mit den richtigen Tools ist die Verwendung von Generika nicht schlecht.Da die Typen bekannt sind, verhält sich die IDE beim Abrufen von Elementen aus der generischen Auflistung so, als ob dieses Objekt bereits ein Objekt des deklarierten Typs ist. Es ist kein Casting erforderlich, damit die IDE den Typ des Objekts kennt ist.
Ein wesentlicher Vorteil von Generika besteht darin, dass sie mit neuen Java 5-Funktionen gut funktionieren. Hier ist ein Beispiel für das Werfen von ganzen Zahlen in a
Set
und das Berechnen der Summe:In diesem Code sind drei neue Java 5-Funktionen enthalten:
Erstens erlauben Generika und Autoboxing von Grundelementen die folgenden Zeilen:
Die Ganzzahl
10
wird automatisch in eineInteger
mit dem Wert von geschrieben10
. (Und das gleiche für42
). DannInteger
wird das in das geworfen, von demSet
bekannt ist, dass esInteger
s hält. Der Versuch, ein zu werfen,String
würde einen Kompilierungsfehler verursachen.Als nächstes werden für jede Schleife alle drei verwendet:
Zunächst werden die
Set
enthaltendenInteger
s in einer for-each-Schleife verwendet. Jedes Element wird als ein deklariertint
und dies ist zulässig, da dasInteger
Element nicht in das Grundelement zurückgeführt wirdint
. Und die Tatsache, dass dieses Unboxing auftritt, ist bekannt, da Generika verwendet wurden, um anzugeben, dassInteger
s in der gehalten wurdenSet
.Generika können der Klebstoff sein, der die in Java 5 eingeführten neuen Funktionen zusammenbringt, und sie machen das Codieren einfach und sicherer. Und die meiste Zeit sind IDEs klug genug, um Ihnen mit guten Vorschlägen zu helfen. Im Allgemeinen wird es also nicht viel mehr tippen.
Und ehrlich gesagt, wie aus dem
Set
Beispiel hervorgeht, kann die Verwendung von Java 5-Funktionen den Code präziser und robuster machen.Bearbeiten - Ein Beispiel ohne Generika
Das Folgende ist eine Illustration des obigen
Set
Beispiels ohne die Verwendung von Generika. Es ist möglich, aber nicht gerade angenehm:(Hinweis: Der obige Code generiert beim Kompilieren eine ungeprüfte Konvertierungswarnung.)
Wenn Sie nicht generische Sammlungen verwenden, sind die Typen, die in die Sammlung eingegeben werden, Objekte vom Typ
Object
. Daher wird in diesem Beispiel eineObject
ist , was wirdadd
in die Menge ed.In den obigen Zeilen ist Autoboxing im Spiel - der primitive
int
Wert10
und42
wird inInteger
Objekte autoboxed , die dem hinzugefügt werdenSet
. Beachten Sie jedoch, dass dieInteger
Objekte alsObject
s behandelt werden, da es keine Typinformationen gibt, die dem Compiler helfen, zu wissen, welchen Typ derSet
erwarten sollte.Dies ist der entscheidende Teil. Der Grund, warum die for-each-Schleife funktioniert, liegt darin, dass
Set
sie dieIterable
Schnittstelle implementiert , dieIterator
, falls vorhanden , eine mit Typinformationen zurückgibt . (Iterator<T>
das heißt.)Da es jedoch keine Typinformationen, die
Set
kehren ein ,Iterator
die die Werte in der Rückkehr wirdSet
alsObject
s, und deshalb ist das Element in der for-Schleife jeden abgerufen werden muß vom Typ seinObject
.Nachdem das aus dem
Object
abgerufen wurdeSet
, muss esInteger
manuell in ein umgewandelt werden, um das Hinzufügen durchzuführen:Hier wird eine Typumwandlung von a
Object
nach a durchgeführtInteger
. In diesem Fall wissen wir, dass dies immer funktionieren wird, aber bei der manuellen Typisierung habe ich immer das Gefühl, dass es sich um fragilen Code handelt, der beschädigt werden könnte, wenn an anderer Stelle eine geringfügige Änderung vorgenommen wird. (Ich habe das Gefühl, dass jede Typografie daraufClassCastException
wartet, passiert zu werden, aber ich schweife ab ...)Das
Integer
ist jetzt in eine entpacktint
und darf die Addition in dieint
Variable durchführentotal
.Ich hoffe, ich konnte veranschaulichen, dass die neuen Funktionen von Java 5 mit nicht generischem Code verwendet werden können, aber es ist nicht so sauber und unkompliziert wie das Schreiben von Code mit generischem Code. Um die neuen Funktionen in Java 5 optimal nutzen zu können, sollte man sich meiner Meinung nach mit Generika befassen, wenn zumindest Überprüfungen zur Kompilierungszeit möglich sind, um zu verhindern, dass ungültige Typecasts zur Laufzeit Ausnahmen auslösen.
quelle
Wenn Sie die Java-Fehlerdatenbank kurz vor der Veröffentlichung von 1.5 durchsuchen würden, würden Sie siebenmal mehr Fehler mit
NullPointerException
als findenClassCastException
. Es scheint also keine großartige Funktion zu sein, Fehler zu finden, oder zumindest Fehler, die nach einem kleinen Rauchtest bestehen bleiben.Für mich ist der große Vorteil von Generika, dass sie wichtige Typinformationen im Code dokumentieren . Wenn ich nicht wollte, dass diese Typinformationen im Code dokumentiert werden, würde ich eine dynamisch typisierte Sprache oder zumindest eine Sprache mit impliziterer Typinferenz verwenden.
Die Sammlungen eines Objekts für sich zu behalten, ist kein schlechter Stil (aber der übliche Stil besteht darin, die Kapselung effektiv zu ignorieren). Es kommt eher darauf an, was Sie tun. Das Übergeben von Sammlungen an "Algorithmen" ist mit Generika etwas einfacher (zur oder vor der Kompilierungszeit) zu überprüfen.
quelle
Generika in Java erleichtern den parametrischen Polymorphismus . Mithilfe von Typparametern können Sie Argumente an Typen übergeben. So wie eine Methode wie ein
String foo(String s)
bestimmtes Verhalten modelliert, nicht nur für eine bestimmte Zeichenfolge, sondern für eine beliebige Zeichenfolges
, soList<T>
modelliert ein Typ wie ein bestimmtes Verhalten, nicht nur für einen bestimmten Typ, sondern für einen beliebigen Typ .List<T>
sagt, dass es für jeden TypT
einen Typ gibt,List
dessen ElementeT
s sind . IstList
also eigentlich ein Typkonstruktor . Es nimmt einen Typ als Argument und erstellt als Ergebnis einen anderen Typ.Hier sind einige Beispiele für generische Typen, die ich jeden Tag verwende. Erstens eine sehr nützliche generische Schnittstelle:
Diese Schnittstelle besagt, dass es für zwei Typen
A
undB
eine Funktion (aufgerufenf
) gibt, die a nimmtA
und a zurückgibtB
. Wenn Sie diese Schnittstelle implementieren,A
undB
kann jede Art sein , das Sie wollen, solange Sie eine Funktion zur Verfügung stellenf
, die die ehemalige nimmt und die letztere. Hier ist eine Beispielimplementierung der Schnittstelle:Vor Generika wurde Polymorphismus durch Unterklassen mit dem
extends
Schlüsselwort erreicht. Mit Generika können wir die Unterklasse tatsächlich beseitigen und stattdessen parametrischen Polymorphismus verwenden. Betrachten Sie beispielsweise eine parametrisierte (generische) Klasse, die zum Berechnen von Hash-Codes für einen beliebigen Typ verwendet wird. Anstatt Object.hashCode () zu überschreiben, würden wir eine generische Klasse wie diese verwenden:Dies ist viel flexibler als die Verwendung von Vererbung, da wir beim Thema der Verwendung von Komposition und parametrischem Polymorphismus bleiben können, ohne spröde Hierarchien zu blockieren.
Javas Generika sind jedoch nicht perfekt. Sie können über Typen abstrahieren, aber Sie können beispielsweise nicht über Typkonstruktoren abstrahieren. Das heißt, Sie können "für jeden Typ T" sagen, aber Sie können nicht "für jeden Typ T sagen, der einen Typparameter A annimmt".
Ich habe hier einen Artikel über diese Grenzen von Java-Generika geschrieben.
Ein großer Gewinn bei Generika ist, dass Sie damit Unterklassen vermeiden können. Unterklassen führen in der Regel zu spröden Klassenhierarchien, deren Erweiterung schwierig ist, und zu Klassen, die einzeln schwer zu verstehen sind, ohne die gesamte Hierarchie zu betrachten.
Wereas vor Generika könnten Sie Klassen wie
Widget
durch erweitertFooWidget
,BarWidget
undBazWidget
mit Generika können Sie eine einzelne generische Klasse ,Widget<A>
die eine nimmtFoo
,Bar
oderBaz
in seinem Konstruktor Sie zu gebenWidget<Foo>
,Widget<Bar>
undWidget<Baz>
.quelle
Generika vermeiden den Leistungseinbruch beim Boxen und Unboxen. Schauen Sie sich grundsätzlich ArrayList vs List <T> an. Beide erledigen die gleichen Kernaufgaben, aber List <T> ist viel schneller, da Sie nicht zum / vom Objekt boxen müssen.
quelle
Der beste Vorteil von Generics ist die Wiederverwendung von Code. Nehmen wir an, Sie haben viele Geschäftsobjekte und schreiben SEHR ähnlichen Code für jede Entität, um dieselben Aktionen auszuführen. (IE Linq to SQL-Operationen).
Mit Generics können Sie eine Klasse erstellen, die mit einem der Typen arbeiten kann, die von einer bestimmten Basisklasse erben, oder eine bestimmte Schnittstelle wie folgt implementieren:
Beachten Sie die Wiederverwendung desselben Dienstes angesichts der verschiedenen Typen in der obigen DoSomething-Methode. Wirklich elegant!
Es gibt viele andere gute Gründe, Generika für Ihre Arbeit zu verwenden. Dies ist mein Favorit.
quelle
Ich mag sie nur, weil sie Ihnen eine schnelle Möglichkeit bieten, einen benutzerdefinierten Typ zu definieren (da ich sie sowieso verwende).
Anstatt beispielsweise eine Struktur zu definieren, die aus einer Zeichenfolge und einer Ganzzahl besteht, und dann eine ganze Reihe von Objekten und Methoden für den Zugriff auf ein Array dieser Strukturen usw. implementieren zu müssen, können Sie einfach ein Wörterbuch erstellen
Und der Compiler / die IDE erledigt den Rest des schweren Hebens. Insbesondere in einem Wörterbuch können Sie den ersten Typ als Schlüssel verwenden (keine wiederholten Werte).
quelle
Typisierte Sammlungen - auch wenn Sie sie nicht verwenden möchten, müssen Sie sie wahrscheinlich aus anderen Bibliotheken und Quellen verarbeiten.
Generische Eingabe bei der Klassenerstellung:
öffentliche Klasse Foo <T> {public T get () ...
Vermeidung von Casting - Ich habe Dinge wie immer nicht gemocht
neuer Komparator {public int compareTo (Objekt o) {if (o Instanz von classIcareAbout) ...
Wo Sie im Wesentlichen nach einer Bedingung suchen, die nur vorhanden sein sollte, weil die Schnittstelle in Objekten ausgedrückt wird.
Meine anfängliche Reaktion auf Generika war ähnlich wie Ihre - "zu chaotisch, zu kompliziert". Ich habe die Erfahrung gemacht, dass man sich nach einer kurzen Verwendung an sie gewöhnt und sich Code ohne sie weniger klar spezifiziert und einfach weniger komfortabel anfühlt. Abgesehen davon verwendet der Rest der Java-Welt sie, so dass Sie irgendwann mit dem Programm fertig werden müssen, oder?
quelle
Um ein gutes Beispiel zu geben. Stellen Sie sich vor, Sie haben eine Klasse namens Foo
Beispiel 1 Nun möchten Sie eine Sammlung von Foo-Objekten haben. Sie haben zwei Optionen, LIst oder ArrayList, die beide auf ähnliche Weise funktionieren.
Wenn ich im obigen Code versuche, eine Klasse von FireTruck anstelle von Foo hinzuzufügen, fügt die ArrayList diese hinzu, aber die generische Liste von Foo führt dazu, dass eine Ausnahme ausgelöst wird.
Beispiel zwei.
Jetzt haben Sie Ihre zwei Array-Listen und möchten jeweils die Bar () -Funktion aufrufen. Da die ArrayList mit Objekten gefüllt ist, müssen Sie diese umwandeln, bevor Sie bar aufrufen können. Da die generische Liste von Foo jedoch nur Foos enthalten kann, können Sie Bar () direkt auf diesen aufrufen.
quelle
Haben Sie noch nie eine Methode (oder eine Klasse) geschrieben, bei der das Schlüsselkonzept der Methode / Klasse nicht eng an einen bestimmten Datentyp der Parameter / Instanzvariablen gebunden war (denken Sie an verknüpfte Liste, Max / Min-Funktionen, binäre Suche)? , etc.).
Wünschten Sie sich nicht jemals, Sie könnten den Algorithmus / Code wiederverwenden, ohne auf die Wiederverwendung durch Ausschneiden und Einfügen zurückzugreifen oder die starke Typisierung zu beeinträchtigen (z. B. möchte ich einen
List
von Strings, keinenList
von Dingen, von denen ich hoffe, dass sie Strings sind!)?Deshalb sollten Sie wollen Generika (oder etwas besser) zu verwenden.
quelle
Vergessen Sie nicht, dass Generika nicht nur von Klassen verwendet werden, sondern auch von Methoden. Nehmen Sie zum Beispiel das folgende Snippet:
Es ist einfach, kann aber sehr elegant verwendet werden. Das Schöne ist, dass die Methode alles zurückgibt, was sie gegeben hat. Dies ist hilfreich, wenn Sie Ausnahmen behandeln, die erneut an den Anrufer zurückgegeben werden müssen:
Der Punkt ist, dass Sie den Typ nicht verlieren, indem Sie ihn durch eine Methode übergeben. Sie können den richtigen Ausnahmetyp anstelle von nur a auslösen
Throwable
, was alles wäre, was Sie ohne Generika tun könnten.Dies ist nur ein einfaches Beispiel für eine Verwendung für generische Methoden. Es gibt noch einige andere nette Dinge, die Sie mit generischen Methoden tun können. Das coolste ist meiner Meinung nach der Typ, der auf Generika schließen lässt. Nehmen Sie das folgende Beispiel (entnommen aus Josh Blochs Effective Java 2nd Edition):
Dies macht nicht viel, verringert jedoch die Unordnung, wenn die generischen Typen lang (oder verschachtelt, dh
Map<String, List<String>>
) sind.quelle
Throwable
aus einem Methodenkörper mit bestimmten deklarierten Ausnahmen werfen können . Die Alternative besteht darin, entweder separate Methoden zu schreiben, um jeden Ausnahmetyp zurückzugeben, oder die Umwandlung selbst mit einer nicht generischen Methode durchzuführen, die a zurückgibtThrowable
. Ersteres ist zu ausführlich und ziemlich nutzlos, und letzteres wird vom Compiler keine Hilfe erhalten. Durch die Verwendung von Generika fügt der Compiler automatisch die richtige Besetzung für Sie ein. Die Frage ist also: Ist das die Komplexität wert?Der Hauptvorteil ist, wie Mitchel betont, die starke Typisierung, ohne dass mehrere Klassen definiert werden müssen.
Auf diese Weise können Sie Dinge tun wie:
Ohne Generika müssten Sie blah [0] auf den richtigen Typ umstellen, um auf seine Funktionen zugreifen zu können.
quelle
Das JVM-Casting wird trotzdem ausgeführt. Es wird implizit Code erstellt, der den generischen Typ als "Objekt" behandelt und Casts für die gewünschte Instanziierung erstellt. Java-Generika sind nur syntaktischer Zucker.
Ich weiß, dass dies eine C # -Frage ist, aber Generika werden auch in anderen Sprachen verwendet und ihre Verwendung / Ziele sind ziemlich ähnlich.
Java-Sammlungen verwenden Generika seit Java 1.5. Ein guter Ort, um sie zu verwenden, ist, wenn Sie Ihr eigenes sammlungsähnliches Objekt erstellen.
Ein Beispiel, das ich fast überall sehe, ist eine Pair-Klasse, die zwei Objekte enthält, aber generisch mit diesen Objekten umgehen muss.
Wann immer Sie diese Pair-Klasse verwenden, können Sie angeben, mit welcher Art von Objekten sie behandelt werden soll, und alle Arten von Cast-Problemen werden zur Kompilierungszeit und nicht zur Laufzeit angezeigt.
Bei Generika können die Grenzen auch mit den Schlüsselwörtern "Super" und "Erweitert" definiert werden. Wenn Sie beispielsweise mit einem generischen Typ arbeiten möchten, aber sicherstellen möchten, dass er eine Klasse namens Foo (mit einer setTitle-Methode) erweitert:
Obwohl es für sich genommen nicht sehr interessant ist, ist es nützlich zu wissen, dass Sie bei jedem Umgang mit einem FooManager wissen, dass er MyClass-Typen verarbeitet und dass MyClass Foo erweitert.
quelle
In der Sun Java-Dokumentation als Antwort auf "Warum sollte ich Generika verwenden?":
"Generics bietet Ihnen die Möglichkeit, dem Compiler den Typ einer Sammlung mitzuteilen, damit dieser überprüft werden kann. Sobald der Compiler den Elementtyp der Sammlung kennt, kann der Compiler überprüfen, ob Sie die Sammlung konsistent verwendet haben, und einfügen Die korrekte Umwandlung von Werten, die aus der Sammlung entfernt werden ... Der Code, der Generika verwendet, ist klarer und sicherer. Der Compiler kann zur Kompilierungszeit überprüfen, ob die Typeinschränkungen zur Laufzeit nicht verletzt werden [Hervorhebung von mir] Wenn das Programm ohne Warnungen kompiliert wird, können wir mit Sicherheit feststellen, dass es zur Laufzeit keine ClassCastException auslöst. Der Nettoeffekt der Verwendung von Generika, insbesondere in großen Programmen, ist eine verbesserte Lesbarkeit und Robustheit . [Hervorhebung meiner] "
quelle
Mit Generika können Sie Objekte erstellen, die stark typisiert sind, ohne jedoch den spezifischen Typ definieren zu müssen. Ich denke, das beste nützliche Beispiel ist die Liste und ähnliche Klassen.
Mit der generischen Liste können Sie eine Listenlistenliste haben, was immer Sie wollen, und Sie können immer auf die starke Typisierung verweisen, Sie müssen nicht konvertieren oder etwas, wie Sie es mit einem Array oder einer Standardliste tun würden.
quelle
Mit Generics können Sie eine starke Typisierung für Objekte und Datenstrukturen verwenden, die in der Lage sein sollten, jedes Objekt aufzunehmen. Außerdem werden mühsame und teure Typecasts beim Abrufen von Objekten aus generischen Strukturen (Boxen / Unboxen) vermieden.
Ein Beispiel, das beide verwendet, ist eine verknüpfte Liste. Was nützt eine verknüpfte Listenklasse, wenn sie nur das Objekt Foo verwenden könnte? Um eine verknüpfte Liste zu implementieren, die jede Art von Objekt verarbeiten kann, müssen die verknüpfte Liste und die Knoten in einer hypothetischen inneren Knotenklasse generisch sein, wenn die Liste nur einen Objekttyp enthalten soll.
quelle
Wenn Ihre Sammlung Werttypen enthält, müssen diese beim Einfügen in die Sammlung nicht an Objekte gepackt / entpackt werden, damit sich Ihre Leistung dramatisch erhöht. Coole Add-Ons wie Resharper können mehr Code für Sie generieren, wie z. B. foreach-Schleifen.
quelle
Ein weiterer Vorteil der Verwendung von Generika (insbesondere bei Sammlungen / Listen) besteht darin, dass Sie die Typprüfung für die Kompilierungszeit erhalten. Dies ist sehr nützlich, wenn Sie eine generische Liste anstelle einer Liste von Objekten verwenden.
quelle
Single meisten Grund ist , sie bieten Typsicherheit
im Gegensatz zu,
als einfaches Beispiel.
quelle
Zusammenfassend lässt sich sagen, dass Sie mit Generika genauer angeben können, was Sie tun möchten (stärkere Eingabe).
Dies hat mehrere Vorteile für Sie:
Da der Compiler mehr darüber weiß, was Sie tun möchten, können Sie viel Typ-Casting weglassen, da er bereits weiß, dass der Typ kompatibel ist.
Dadurch erhalten Sie auch früheres Feedback zur Richtigkeit Ihres Programms. Dinge, die zuvor zur Laufzeit fehlgeschlagen wären (z. B. weil ein Objekt nicht in den gewünschten Typ umgewandelt werden konnte), schlagen jetzt zur Kompilierungszeit fehl und Sie können den Fehler beheben, bevor Ihre Testabteilung einen kryptischen Fehlerbericht einreicht.
Der Compiler kann weitere Optimierungen vornehmen, z. B. das Vermeiden von Boxen usw.
quelle
Ein paar Dinge, die hinzugefügt / erweitert werden müssen (aus der Sicht von .NET):
Mit generischen Typen können Sie rollenbasierte Klassen und Schnittstellen erstellen. Dies wurde bereits in grundlegenderen Begriffen gesagt, aber ich finde, Sie beginnen, Ihren Code mit Klassen zu entwerfen, die typunabhängig implementiert sind - was zu hoch wiederverwendbarem Code führt.
Generische Argumente zu Methoden können dasselbe bewirken, aber sie helfen auch dabei, das Prinzip "Tell Don't Ask" auf das Casting anzuwenden, dh "gib mir, was ich will, und wenn du nicht kannst, sagst du mir warum".
quelle
Ich verwende sie zum Beispiel in einem mit SpringORM und Hibernate implementierten GenericDao, das so aussieht
Durch die Verwendung von Generika zwingen meine Implementierungen dieser DAOs den Entwickler, ihnen nur die Entitäten zu übergeben, für die sie entwickelt wurden, indem sie nur das GenericDao unterordnen
Mein kleines Framework ist robuster (habe Dinge wie Filtern, Lazy-Loading, Suchen). Ich habe es hier nur vereinfacht, um Ihnen ein Beispiel zu geben
Ich sagte, wie Steve und Sie, am Anfang "Zu chaotisch und kompliziert", aber jetzt sehe ich seine Vorteile
quelle
Offensichtliche Vorteile wie "Typensicherheit" und "kein Gießen" werden bereits erwähnt, sodass ich vielleicht über einige andere "Vorteile" sprechen kann, von denen ich hoffe, dass sie helfen.
Erstens sind Generika ein sprachunabhängiges Konzept, und IMO ist es möglicherweise sinnvoller, wenn Sie gleichzeitig über regulären (Laufzeit-) Polymorphismus nachdenken.
Zum Beispiel hat der Polymorphismus, wie wir ihn aus dem objektorientierten Design kennen, einen Laufzeitbegriff, bei dem das Aufruferobjekt zur Laufzeit während der Programmausführung ermittelt wird und die entsprechende Methode je nach Laufzeittyp entsprechend aufgerufen wird. Bei Generika ist die Idee etwas ähnlich, aber alles geschieht zur Kompilierungszeit. Was bedeutet das und wie nutzen Sie es?
(Bleiben wir bei generischen Methoden, um sie kompakt zu halten.) Dies bedeutet, dass Sie dieselbe Methode immer noch für separate Klassen verwenden können (wie zuvor in polymorphen Klassen), diesmal jedoch vom Compiler automatisch generiert werden. Dies hängt von den festgelegten Typen ab zur Kompilierungszeit. Sie parametrisieren Ihre Methoden anhand des Typs, den Sie zur Kompilierungszeit angeben. Anstatt die Methoden für jeden einzelnen Typ von Grund auf neu zu schreiben, wie Sie es beim Laufzeitpolymorphismus tun (Methodenüberschreibung), lassen Sie Compiler die Arbeit während der Kompilierung erledigen. Dies hat einen offensichtlichen Vorteil, da Sie nicht alle möglichen Typen ableiten müssen, die in Ihrem System verwendet werden könnten, wodurch es ohne Codeänderung weitaus skalierbarer wird.
Der Unterricht funktioniert ziemlich ähnlich. Sie parametrisieren den Typ und der Code wird vom Compiler generiert.
Sobald Sie die Idee der "Kompilierungszeit" haben, können Sie "begrenzte" Typen verwenden und einschränken, was über Klassen / Methoden als parametrisierter Typ übergeben werden kann. Sie können also steuern, was durchlaufen werden soll. Dies ist eine leistungsstarke Sache, insbesondere wenn Sie ein Framework haben, das von anderen Personen verwendet wird.
Niemand außer MyObject kann jetzt etwas anderes einstellen.
Außerdem können Sie Typbeschränkungen für Ihre Methodenargumente "erzwingen", was bedeutet, dass Sie sicherstellen können, dass beide Methodenargumente vom gleichen Typ abhängen.
Hoffe, das alles macht Sinn.
quelle
Ich habe einmal einen Vortrag zu diesem Thema gehalten. Sie finden meine Folien, meinen Code und meine Audioaufzeichnung unter http://www.adventuresinsoftware.com/generics/ .
quelle
Die Verwendung von Generika für Sammlungen ist einfach und sauber. Selbst wenn Sie überall darauf herumstochern, ist der Gewinn aus den Sammlungen für mich ein Gewinn.
vs.
oder
Das allein ist die marginalen "Kosten" von Generika wert, und Sie müssen kein generischer Guru sein, um dies zu nutzen und Wert zu erhalten.
quelle
quelle
Generika bieten Ihnen auch die Möglichkeit, wiederverwendbarere Objekte / Methoden zu erstellen und gleichzeitig typspezifische Unterstützung zu bieten. In einigen Fällen erhalten Sie auch viel Leistung. Ich kenne nicht die vollständige Spezifikation für die Java-Generika, aber in .NET kann ich Einschränkungen für den Type-Parameter angeben, z. B. Implementiert eine Schnittstelle, einen Konstruktor und eine Ableitung.
quelle