Was ist cool an Generika, warum sie verwenden?

85

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.

MrBoJangles
quelle
1
Duplikat von stackoverflow.com/questions/77632/… . Aber einfache Antwort, auch wenn Sammlungen nicht Teil Ihrer API sind, mag ich kein unnötiges Casting, selbst in der internen Implementierung.
Matthew Flaschen
Die Frage ist ziemlich ähnlich, aber ich glaube nicht, dass die akzeptierte Antwort diese beantwortet.
Tom Hawtin - Tackline
1
Überprüfen Sie
Clint Miller
4
@MatthewFlaschen komisch, dein Duplikat verweist auf diese Frage ... Panne in der Matrix!
JCollum
@jcollum, ich denke, die ursprüngliche Frage, zu der ich diesen Kommentar gepostet habe, wurde mit dieser Frage zusammengeführt.
Matthew Flaschen

Antworten:

123
  • Ermöglicht das Schreiben von Code / die Verwendung typsicherer Bibliotheksmethoden, dh eine Liste <Zeichenfolge> ist garantiert eine Liste von Zeichenfolgen.
  • Aufgrund der Verwendung von Generika kann der Compiler Code zur Kompilierungszeit aus Gründen der Typensicherheit überprüfen. Versuchen Sie also, ein int in diese Liste von Zeichenfolgen einzufügen? Die Verwendung einer ArrayList würde dazu führen, dass dies ein weniger transparenter Laufzeitfehler ist.
  • Schneller als die Verwendung von Objekten, da entweder das Boxen / Entpacken (bei dem .net Werttypen in Referenztypen konvertieren muss oder umgekehrt ) oder das Umwandeln von Objekten in den erforderlichen Referenztyp vermieden wird.
  • Ermöglicht das Schreiben von Code, der auf viele Typen mit demselben zugrunde liegenden Verhalten anwendbar ist, dh ein Dictionary <string, int> verwendet denselben zugrunde liegenden Code wie ein Dictionary <DateTime, double>; Mit Generika musste das Framework-Team nur einen Code schreiben, um beide Ergebnisse mit den oben genannten Vorteilen zu erzielen.
ljs
quelle
9
Ah, sehr nett, und ich mag Aufzählungslisten. Ich denke, dies ist die vollständigste und prägnanteste Antwort, die es bisher gab.
MrBoJangles
Die ganze Erklärung ist im Kontext "Sammlungen". Ich suche einen breiteren Blick. irgendwelche Gedanken?
jungle_mole
Gute Antwort Ich möchte nur 2 weitere Vorteile hinzufügen. Die generische Einschränkung bietet neben 1 2 Vorteile. Sie können die Eigenschaften des eingeschränkten Typs im generischen Typ verwenden (z. B. wenn T: IComparable die Verwendung von CompareTo vorsieht). 2- Die Person, die schreibt der Code, nachdem Sie wissen, was zu tun ist
Hamit YILDIRIM
52

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:

class MyObjectList  {
   MyObject get(int index) {...}
}
class MyOtherObjectList  {
   MyOtherObject get(int index) {...}
}
class AnotherObjectList  {
   AnotherObject get(int index) {...}
}

Ich kann eine wiederverwendbare Klasse erstellen ... (für den Fall, dass Sie die Raw-Sammlung aus irgendeinem Grund nicht verwenden möchten)

class MyList<T> {
   T get(int index) { ... }
}

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 a Reference<T>, die mit anderen Klassen interagieren müssen. Wollen Sie wirklich erweitern Callable<T>und Future<T>und alle anderen zugehörigen Klasse typsichere Versionen zu erstellen?

Ich nicht.

James Schek
quelle
1
Ich bin mir nicht sicher ob ich das verstehe. Ich schlage nicht vor, dass Sie "MyObject" -Wrapper erstellen, das wäre schrecklich. Ich schlage vor, dass Sie ein Garage-Objekt schreiben, wenn Ihre Sammlung von Objekten eine Sammlung von Autos ist. Es hätte nicht bekommen (Auto), es hätte Methoden wie buyFuel (100), die 100 Dollar Kraftstoff kaufen und unter den Autos verteilen würden, die es am dringendsten brauchen. Ich spreche von echten Business-Klassen, nicht nur von "Wrappern". Zum Beispiel sollten Sie sie fast nie außerhalb der Sammlung bekommen (Auto) oder durchlaufen - Sie fragen kein Objekt nach seinen Mitgliedern, sondern Sie bitten es, eine Operation für Sie durchzuführen.
Bill K
Auch in Ihrer Garage sollten Sie Typensicherheit haben. Entweder hast du List <Cars>, ListOfCars oder du wirfst überall. Ich habe nicht das Bedürfnis, den Typ mehr als die erforderlichen Mindestzeiten anzugeben.
James Schek
Ich bin damit einverstanden, dass Typensicherheit nett wäre, aber es ist viel weniger hilfreich, da es auf die Garagenklasse beschränkt ist. Es ist dann gekapselt und leicht zu steuern, daher ist mein Punkt, dass es Ihnen diesen kleinen Vorteil auf Kosten der neuen Syntax bietet. Es ist wie eine Änderung der US-Verfassung, um ein rotes Licht illegal zu machen - Ja, es sollte immer illegal sein, aber rechtfertigt es eine Änderung? Ich denke auch, dass es die Leute dazu ermutigt, für diesen Fall KEINE Kapselung zu verwenden, was tatsächlich vergleichsweise schädlich ist.
Bill K
Bill - Sie haben vielleicht einen Punkt darüber, dass Sie die Kapselung nicht verwenden, aber der Rest Ihrer Argumentation ist ein schlechter Vergleich. Die Kosten für das Erlernen der neuen Syntax sind in diesem Fall minimal (vergleichen Sie dies mit Lambdas in C #). Ein Vergleich mit einer Änderung der Verfassung macht keinen Sinn. Die Verfassung hat nicht die Absicht, eine Verkehrsordnung festzulegen. Es ist eher so, als würde man zu einer gedruckten Version mit Serifenschrift auf Plakatkarton wechseln, anstatt zu einer handgeschriebenen Kopie auf Pergament. Es ist die gleiche Bedeutung, aber es ist viel klarer und leichter zu lesen.
James Schek
Das Beispiel macht das Konzept praktischer. Dank dafür.
RayLoveless
22

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 ClassCastExceptions, 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.

Jedes Mal, wenn ich Generika anschaue, bekomme ich Kopfschmerzen. Ich finde, das Beste an Java ist seine Einfachheit und minimale Syntax, und Generika sind nicht einfach und fügen eine erhebliche Menge neuer Syntax hinzu.

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 a HashMap. Der Code, den ich eingeben müsste, lautet:

Map<String, Integer> m = new HashMap<String, Integer>();

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+Space

Zwar musste ich HashMapaus 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 Setund das Berechnen der Summe:

Set<Integer> set = new HashSet<Integer>();
set.add(10);
set.add(42);

int total = 0;
for (int i : set) {
  total += i;
}

In diesem Code sind drei neue Java 5-Funktionen enthalten:

Erstens erlauben Generika und Autoboxing von Grundelementen die folgenden Zeilen:

set.add(10);
set.add(42);

Die Ganzzahl 10wird automatisch in eine Integermit dem Wert von geschrieben 10. (Und das gleiche für 42). Dann Integerwird das in das geworfen, von dem Setbekannt ist, dass es Integers hält. Der Versuch, ein zu werfen, Stringwürde einen Kompilierungsfehler verursachen.

Als nächstes werden für jede Schleife alle drei verwendet:

for (int i : set) {
  total += i;
}

Zunächst werden die Setenthaltenden Integers in einer for-each-Schleife verwendet. Jedes Element wird als ein deklariert intund dies ist zulässig, da das IntegerElement nicht in das Grundelement zurückgeführt wird int. Und die Tatsache, dass dieses Unboxing auftritt, ist bekannt, da Generika verwendet wurden, um anzugeben, dass Integers in der gehalten wurden Set.

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 SetBeispiel 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 SetBeispiels ohne die Verwendung von Generika. Es ist möglich, aber nicht gerade angenehm:

Set set = new HashSet();
set.add(10);
set.add(42);

int total = 0;
for (Object o : set) {
  total += (Integer)o;
}

(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 eine Objectist , was wird addin die Menge ed.

set.add(10);
set.add(42);

In den obigen Zeilen ist Autoboxing im Spiel - der primitive intWert 10und 42wird in IntegerObjekte autoboxed , die dem hinzugefügt werden Set. Beachten Sie jedoch, dass die IntegerObjekte als Objects behandelt werden, da es keine Typinformationen gibt, die dem Compiler helfen, zu wissen, welchen Typ der Seterwarten sollte.

for (Object o : set) {

Dies ist der entscheidende Teil. Der Grund, warum die for-each-Schleife funktioniert, liegt darin, dass Setsie die IterableSchnittstelle implementiert , die Iterator, falls vorhanden , eine mit Typinformationen zurückgibt . ( Iterator<T>das heißt.)

Da es jedoch keine Typinformationen, die Setkehren ein , Iteratordie die Werte in der Rückkehr wird Setals Objects, und deshalb ist das Element in der for-Schleife jeden abgerufen werden muß vom Typ sein Object.

Nachdem das aus dem Objectabgerufen wurde Set, muss es Integermanuell in ein umgewandelt werden, um das Hinzufügen durchzuführen:

  total += (Integer)o;

Hier wird eine Typumwandlung von a Objectnach a durchgeführt Integer. 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 darauf ClassCastExceptionwartet, passiert zu werden, aber ich schweife ab ...)

Das Integerist jetzt in eine entpackt intund darf die Addition in die intVariable durchführen total.

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.

Coobird
quelle
Die Integration mit der erweiterten for-Schleife ist wirklich nett (obwohl ich denke, dass die verdammte Schleife kaputt ist, weil ich bei einer nicht generischen Sammlung nicht genau dieselbe Syntax verwenden kann - sie sollte nur die cast / autobox für int verwenden, sie hat alle Informationen, die es braucht!), aber Sie haben Recht, die Hilfe in der IDE ist wahrscheinlich gut. Ich weiß immer noch nicht, ob es ausreicht, um die zusätzliche Syntax zu rechtfertigen, aber zumindest kann ich davon profitieren (was meine Frage beantwortet).
Bill K
@ Bill K: Ich habe ein Beispiel für die Verwendung von Java 5-Funktionen ohne Verwendung von Generika hinzugefügt.
Coobird
Das ist ein guter Punkt, den ich nicht benutzt hatte (ich erwähnte, dass ich seit Jahren auf 1.4 und früh feststecke). Ich bin damit einverstanden, dass es Vorteile gibt, aber die Schlussfolgerungen, zu denen ich komme, sind, dass A) sie für Menschen, die OO nicht richtig verwenden und für diejenigen, die dies tun, weniger nützlich sind, und B) die Vorteile, die Sie ' Wir haben die Vorteile aufgelistet, aber kaum genug wert, um auch nur eine kleine Syntaxänderung zu rechtfertigen, geschweige denn die großen Änderungen, die erforderlich sind, um Generika, wie sie in Java implementiert sind, wirklich zu verbessern. Aber zumindest gibt es Vorteile.
Bill K
15

Wenn Sie die Java-Fehlerdatenbank kurz vor der Veröffentlichung von 1.5 durchsuchen würden, würden Sie siebenmal mehr Fehler mit NullPointerExceptionals finden ClassCastException. 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.

Tom Hawtin - Tackline
quelle
6
Es wird nicht nur dokumentiert (was für sich genommen ein großer Gewinn ist), sondern auch vom Compiler erzwungen.
James Schek
Guter Punkt, aber wird das nicht auch dadurch erreicht, dass die Sammlung in eine Klasse eingeschlossen wird, die "eine Sammlung dieses Objekttyps" definiert?
Bill K
1
James: Ja, darum geht es im Code .
Tom Hawtin - Tackline
Ich glaube nicht, dass ich gute Bibliotheken kenne, die Sammlungen für "Algorithmen" verwenden (was möglicherweise impliziert, dass Sie über "Funktionen" sprechen, was mich zu der Annahme führt, dass sie eine Methode im Objekt der Sammlung sein sollten). Ich denke, dass das JDK fast immer ein schönes, sauberes Array extrahiert, das eine Kopie der Daten ist, damit es nicht verfälscht werden kann - zumindest nicht selten.
Bill K
Ist ein Objekt, das genau beschreibt, wie die Sammlung verwendet wird, und die Einschränkung ihrer Verwendung nicht eine VIEL bessere Form der In-Code-Dokumentation? Ich finde die Informationen in einem Generikum in gutem Code extrem redundant.
Bill K
11

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 Zeichenfolge s, so List<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 Typ Teinen Typ gibt, Listdessen Elemente Ts sind . Ist Listalso 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:

public interface F<A, B> {
  public B f(A a);
}

Diese Schnittstelle besagt, dass es für zwei Typen Aund Beine Funktion (aufgerufen f) gibt, die a nimmt Aund a zurückgibt B. Wenn Sie diese Schnittstelle implementieren, Aund Bkann jede Art sein , das Sie wollen, solange Sie eine Funktion zur Verfügung stellen f, die die ehemalige nimmt und die letztere. Hier ist eine Beispielimplementierung der Schnittstelle:

F<Integer, String> intToString = new F<Integer, String>() {
  public String f(int i) {
    return String.valueOf(i);
  }
}

Vor Generika wurde Polymorphismus durch Unterklassen mit dem extendsSchlü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:

public final class Hash<A> {
  private final F<A, Integer> hashFunction;

  public Hash(final F<A, Integer> f) {
    this.hashFunction = f;
  }

  public int hash(A a) {
    return hashFunction.f(a);
  }
}

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 Widgetdurch erweitert FooWidget, BarWidgetund BazWidgetmit Generika können Sie eine einzelne generische Klasse , Widget<A>die eine nimmt Foo, Baroder Bazin seinem Konstruktor Sie zu geben Widget<Foo>, Widget<Bar>und Widget<Baz>.

Apocalisp
quelle
Es wäre ein guter Punkt für die Unterklasse, wenn Java keine Schnittstellen hätte. Wäre es nicht richtig, wenn FooWidget, BarWidtet und BazWidget Widget implementieren? Ich könnte mich jedoch irren. Aber das ist ein wirklich guter Punkt, wenn ich falsch liege.
Bill K
Warum benötigen Sie eine dynamische generische Klasse, um Hashwerte zu berechnen? Wäre eine statische Funktion mit geeigneten Parametern für den Job nicht effizienter?
Ant_222
8

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.

Darren Kopp
quelle
Das ist das Wesentliche, nicht wahr? Nett.
MrBoJangles
Nun, nicht ganz; Sie sind nützlich für die Typensicherheit + das Vermeiden von Abgüssen für Referenztypen, ohne dass das Ein- und Auspacken überhaupt erfolgt.
ljs
Die folgende Frage lautet also: "Warum sollte ich jemals eine ArrayList verwenden wollen?" Und wenn ich eine Antwort riskiere, würde ich sagen, dass ArrayLists in Ordnung sind, solange Sie nicht erwarten, dass sie ihre Werte viel ein- und auspacken. Bemerkungen?
MrBoJangles
Nein, sie sind ab .net 2 nicht mehr vorhanden. nur nützlich für die Abwärtskompatibilität. ArrayLists sind langsamer, nicht typsicher und erfordern entweder Boxing / Unboxing oder Casting.
ljs
Wenn Sie keine Wertetypen (z. B. Ints oder Strukturen) speichern, gibt es bei Verwendung von ArrayList kein Boxing - die Klassen sind bereits 'Objekt'. Die Verwendung von List <T> ist aus Sicherheitsgründen immer noch viel besser. Wenn Sie wirklich Objekte speichern möchten, ist die Verwendung von List <Objekt> besser.
Wilka
6

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:

public interface IEntity
{

}

public class Employee : IEntity
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int EmployeeID { get; set; }
}

public class Company : IEntity
{
    public string Name { get; set; }
    public string TaxID { get; set }
}

public class DataService<ENTITY, DATACONTEXT>
    where ENTITY : class, IEntity, new()
    where DATACONTEXT : DataContext, new()
{

    public void Create(List<ENTITY> entities)
    {
        using (DATACONTEXT db = new DATACONTEXT())
        {
            Table<ENTITY> table = db.GetTable<ENTITY>();

            foreach (ENTITY entity in entities)
                table.InsertOnSubmit (entity);

            db.SubmitChanges();
        }
    }
}

public class MyTest
{
    public void DoSomething()
    {
        var dataService = new DataService<Employee, MyDataContext>();
        dataService.Create(new Employee { FirstName = "Bob", LastName = "Smith", EmployeeID = 5 });
        var otherDataService = new DataService<Company, MyDataContext>();
            otherDataService.Create(new Company { Name = "ACME", TaxID = "123-111-2233" });

    }
}

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.

Dean Poulin
quelle
5

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

Dictionary<int, string> dictionary = new Dictionary<int, string>();

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).

Tom Kidd
quelle
5
  • 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?


1
Um hinsichtlich der Vermeidung von Casting genau zu sein: Das Casting erfolgt gemäß der Sun-Dokumentation, wird jedoch vom Compiler durchgeführt. Sie müssen nur vermeiden, Casts in Ihren Code zu schreiben.
Demi

Vielleicht stecke ich in einer langen Reihe von Unternehmen fest, die nicht bereit sind, über 1,4 hinauszugehen. Wer weiß, ich kann mich vor meinem 5. Lebensjahr zurückziehen. Ich habe in letzter Zeit auch darüber nachgedacht, dass das Gabeln eines "SimpleJava" ein interessantes Konzept sein könnte - Java wird weiterhin Funktionen hinzufügen und für einen Großteil des Codes, den ich gesehen habe, wird es keine gesunde Sache sein. Ich habe bereits mit Leuten zu tun, die keine Schleife codieren können - ich würde es hassen, wenn sie versuchen würden, Generika in ihre sinnlos abgewickelten Schleifen zu injizieren.
Bill K

@ Bill K: Nur wenige Menschen müssen die volle Leistung von Generika nutzen. Dies ist nur erforderlich, wenn Sie eine Klasse schreiben, die selbst generisch ist.
Eddie

5

Um ein gutes Beispiel zu geben. Stellen Sie sich vor, Sie haben eine Klasse namens Foo

public class Foo
{
   public string Bar() { return "Bar"; }
}

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.

Arraylist al = new ArrayList();
List<Foo> fl = new List<Foo>();

//code to add Foos
al.Add(new Foo());
f1.Add(new Foo());

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.

foreach(object o in al)
{
    Foo f = (Foo)o;
    f.Bar();
}

foreach(Foo f in fl)
{
   f.Bar();
}

Ich vermute, Sie haben geantwortet, bevor Sie die Frage gelesen haben. Das sind die beiden Fälle, die mir nicht wirklich helfen (in Frage gestellt). Gibt es andere Fälle, in denen es etwas Nützliches tut?
Bill K

4

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 Listvon Strings, keinen Listvon Dingen, von denen ich hoffe, dass sie Strings sind!)?

Deshalb sollten Sie wollen Generika (oder etwas besser) zu verwenden.


Ich musste nie wiederverwenden / einfügen für solche Dinge (Sie sind sich nicht sicher, wie das überhaupt passieren würde?) Sie implementieren eine Schnittstelle, die Ihren Anforderungen entspricht, und verwenden diese Schnittstelle. Der seltene Fall, in dem Sie dies nicht tun können, ist, wenn Sie ein reines Dienstprogramm (wie die Sammlungen) schreiben, und für diese (wie ich in der Frage beschrieben habe) war es wirklich nie ein Problem. Ich mache Kompromisse beim starken Tippen - genau wie Sie es müssen, wenn Sie Reflexion verwenden. Weigern Sie sich absolut, Reflexion zu verwenden, weil Sie nicht stark tippen können? (Wenn ich Reflektion verwenden muss, kapsle ich es einfach wie eine Sammlung).
Bill K

3

Vergessen Sie nicht, dass Generika nicht nur von Klassen verwendet werden, sondern auch von Methoden. Nehmen Sie zum Beispiel das folgende Snippet:

private <T extends Throwable> T logAndReturn(T t) {
    logThrowable(t); // some logging method that takes a Throwable
    return t;
}

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:

    ...
} catch (MyException e) {
    throw logAndReturn(e);
}

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):

...
Map<String, Integer> myMap = createHashMap();
...
public <K, V> Map<K, V> createHashMap() {
    return new HashMap<K, V>();
}

Dies macht nicht viel, verringert jedoch die Unordnung, wenn die generischen Typen lang (oder verschachtelt, dh Map<String, List<String>>) sind.


Ich denke nicht, dass dies bei Ausnahmen zutrifft. Es bringt mich zum Nachdenken, aber ich bin zu 95% sicher, dass Sie genau die gleichen Ergebnisse erzielen würden, ohne Generika (und klareren Code) zu verwenden. Typinformationen sind Teil des Objekts und nicht das, in das Sie es umgewandelt haben. Ich bin versucht, es zu versuchen - vielleicht werde ich morgen bei der Arbeit sein und mich bei Ihnen melden. Sagen Sie was, ich werde es versuchen und wenn Sie Recht haben, werde ich es durchgehen Ihre Liste der Beiträge und stimmen Sie für 5 Ihrer Beiträge ab :) Wenn nicht, sollten Sie berücksichtigen, dass die einfache Verfügbarkeit von Generika dazu geführt hat, dass Sie Ihren Code ohne Vorteil kompliziert haben.
Bill K

Dies erhöht zwar die Komplexität. Ich denke jedoch, dass es einen Nutzen hat. Das Problem ist, dass Sie kein einfaches Altes Throwableaus 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ückgibt Throwable. 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?
Jigawot

Oh ich verstehe. So wird vermieden, dass die Besetzung herauskommt ... guter Punkt. Ich schulde dir 5.
Bill K

2

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:

List<SomeCustomClass> blah = new List<SomeCustomClass>();
blah[0].SomeCustomFunction();

Ohne Generika müssten Sie blah [0] auf den richtigen Typ umstellen, um auf seine Funktionen zugreifen zu können.


Wenn ich die Wahl habe, würde ich lieber nicht als besetzen.
MrBoJangles

Starke Typisierung ist imho leicht der beste Aspekt von Generika, insbesondere angesichts der Typüberprüfung zur Kompilierungszeit, die dies ermöglicht.
ljs

2

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.


2

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.

class Pair<F, S> {
    public final F first;
    public final S second;

    public Pair(F f, S s)
    { 
        first = f;
        second = s;   
    }
}  

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:

public class FooManager <F extends Foo>{
    public void setTitle(F foo, String title) {
        foo.setTitle(title);
    }
}

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.


2

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] "


Ich habe noch nie einen Fall gefunden, in dem die generische Eingabe von Sammlungen meinem Code geholfen hätte. Meine Sammlungen sind eng gefasst. Deshalb habe ich folgendes formuliert: "Kann es mir helfen?". Als Nebenfrage sagen Sie jedoch, dass Ihnen dies gelegentlich passiert ist, bevor Sie mit der Verwendung von Generika begonnen haben? (Einfügen des falschen Objekttyps in Ihre Sammlung). Es scheint mir eine Lösung ohne wirkliches Problem zu sein, aber vielleicht habe ich einfach Glück.
Bill K

@ Bill K: Wenn der Code streng kontrolliert wird (dh nur von Ihnen verwendet wird), werden Sie vielleicht nie auf dieses Problem stoßen. Das ist großartig! Die größten Risiken bestehen in großen Projekten, an denen mehrere Personen arbeiten, IMO. Es ist ein Sicherheitsnetz und eine "Best Practice".
Demi

@ Bill K: Hier ist ein Beispiel. Angenommen, Sie haben eine Methode, die eine ArrayList zurückgibt. Angenommen, die ArrayList ist keine generische Sammlung. Jemandes Code nimmt diese ArrayList und versucht, eine Operation für seine Elemente auszuführen. Das einzige Problem besteht darin, dass Sie die ArrayList mit BillTypes gefüllt haben und der Verbraucher versucht, mit ConsumerTypes zu arbeiten. Dies wird kompiliert, aber zur Laufzeit in die Luft gesprengt. Wenn Sie generische Sammlungen verwenden, treten stattdessen Compilerfehler auf, mit denen Sie viel besser umgehen können.
Demi

@ Bill K: Ich habe mit Leuten mit sehr unterschiedlichen Fähigkeiten gearbeitet. Ich habe nicht den Luxus zu wählen, mit wem ich ein Projekt teile. Daher ist mir die Typensicherheit sehr wichtig. Ja, ich habe Fehler gefunden (und behoben), indem ich Code in Generics konvertiert habe.
Eddie

1

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.


1

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.


1

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.


1

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.


1

Single meisten Grund ist , sie bieten Typsicherheit

List<Customer> custCollection = new List<Customer>;

im Gegensatz zu,

object[] custCollection = new object[] { cust1, cust2 };

als einfaches Beispiel.


Typensicherheit, eine Diskussion für sich. Vielleicht sollte jemand eine Frage dazu stellen.
MrBoJangles

Ja, aber was deutest du an? Der springende Punkt bei der Typensicherheit?
Vin

1

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.


1

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".


1

Ich verwende sie zum Beispiel in einem mit SpringORM und Hibernate implementierten GenericDao, das so aussieht

public abstract class GenericDaoHibernateImpl<T> 
    extends HibernateDaoSupport {

    private Class<T> type;

    public GenericDaoHibernateImpl(Class<T> clazz) {
        type = clazz;
    }

    public void update(T object) {
        getHibernateTemplate().update(object);
    }

    @SuppressWarnings("unchecked")
    public Integer count() {
    return ((Integer) getHibernateTemplate().execute(
        new HibernateCallback() {
            public Object doInHibernate(Session session) {
                    // Code in Hibernate for getting the count
                }
        }));
    }
  .
  .
  .
}

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

public class UserDaoHibernateImpl extends GenericDaoHibernateImpl<User> {
    public UserDaoHibernateImpl() {
        super(User.class);     // This is for giving Hibernate a .class
                               // work with, as generics disappear at runtime
    }

    // Entity specific methods here
}

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


1

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.

public interface Foo<T extends MyObject> extends Hoo<T>{
    ...
}

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.

public <T extends MyObject> foo(T t1, T t2){
    ...
}   

Hoffe, das alles macht Sinn.



0

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.

List<Stuff> stuffList = getStuff();
for(Stuff stuff : stuffList) {
    stuff.do();
}

vs.

List stuffList = getStuff();
Iterator i = stuffList.iterator();
while(i.hasNext()) {
    Stuff stuff = (Stuff)i.next();
    stuff.do();
}

oder

List stuffList = getStuff();
for(int i = 0; i < stuffList.size(); i++) {
    Stuff stuff = (Stuff)stuffList.get(i);
    stuff.do();
}

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.


1
Ohne Generika können Sie Folgendes tun: List stuffList = getStuff (); for (Object stuff: stuffList) {((Stuff) stuff) .doStuff (); }, was nicht viel anders ist.

quelle
0

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.

Eric Schneider
quelle