Generika und Typlöschung

10

Generika in Java werden mithilfe der Typlöschung implementiert. Laut JLS war die Inspiration die Abwärtskompatibilität. Wo wie auf der anderen Seite C # Generika reifizierbar sind.

Was sind theoretisch die Vor- und Nachteile von Generika als "Löschung" oder "nachprüfbar"?

Fehlt Java auf etwas?

Jatin
quelle

Antworten:

8

Nun, die Motivation (Abwärtskompatibilität) ist sowohl ein Vorteil als auch ein Nachteil. Es ist ein Nachteil, weil wir alle lieber reifizierbare Typen hätten, aber der zu zahlende Preis war hoch. Berücksichtigen Sie die Entwurfsoptionen in C #. Sie haben reifizierbare Typen, aber jetzt haben sie doppelte APIs. Stellen Sie sich also eine Java-API vor, bei der wir auch doppelte APIs für jede parametrisierte Klasse hatten. Stellen Sie sich nun vor, Sie portieren Tausende von Codezeilen von Legacy-Klassen auf die neuen generischen Klassen. Wer würde doppelte APIs nicht als Nachteil betrachten? Aber hey, sie haben reifizierbare Typen!

Die Hauptmotivation war also "Evolution, nicht Revolution". Und logischerweise hat jede Entscheidung Kompromisse.

Neben den anderen genannten Nachteilen können wir auch die Tatsache hinzufügen, dass das Löschen von Typen zum Zeitpunkt der Kompilierung schwierig zu begründen sein kann, da nicht ersichtlich ist, dass bestimmte Typen entfernt werden, und dies zu sehr seltsamen und schwer zu findenden Fehlern führt.

Die Existenz von Bridge-Methoden (diese vom Compiler syntaktisch generierten Methoden zur Wahrung der Binärkompatibilität) kann ebenfalls als Nachteil angesehen werden. Und dies kann genau einer der Gründe für Fehler sein, die ich im vorherigen Absatz erwähnt habe.

Hauptnachteile ergeben sich aus der bereits offensichtlichen Tatsache, dass es für generische Typen eine einzelne Klasse und nicht mehrere Klassen gibt. Als weiteres Beispiel gilt, dass das Überladen einer Methode mit derselben generischen Klasse in Java fehlschlägt:

public void doSomething(List<One>);
public void doSomething(List<Two>);

Etwas , das als Nachteil reifiable Typen (zumindest in C #) ist die Tatsache , dass sie Ursache gesehen werden könnte Code Explosion . Zum Beispiel List<int>ist eine Klasse und a List<double>ist eine andere völlig anders, wie es a List<string>und a ist List<MyType>. Daher müssen Klassen zur Laufzeit definiert werden, was zu einer Explosion von Klassen führt und wertvolle Ressourcen verbraucht, während sie generiert werden.

In Anbetracht der Tatsache, dass es nicht möglich ist, ein new T()in Java zu definieren , was in einer anderen Antwort erwähnt wird, ist es auch interessant zu berücksichtigen, dass dies nicht nur eine Frage der Typlöschung ist. Es erfordert auch die Existenz eines Standardkonstruktors, weshalb C # hierfür eine "neue Einschränkung" erfordert. (Siehe Warum neues T () in Java nicht möglich ist , von Alex Buckley).

edalorzo
quelle
3
Ich glaube, ich habe Recht, wenn ich sage, dass Sie in der CLR für Referenztypen T nur eine Kopie des Class<T>Codes für alle Ts erhalten. plus eine zusätzliche Kopie für jeden Werttyp T tatsächlich verwendet wird .
AakashM
@AakashM: Das ist richtig, es gibt eine einzelne Referenztypklasse pro Generikum, jedoch erstellt jeder Werttyp seine eigene Version. Dies ist darauf zurückzuführen, dass basierend auf dem Typ spezialisierter Speicherplatz bereitgestellt wird. Alle Referenzen belegen denselben Speicher, Werttypen belegen jedoch unterschiedlichen Speicher pro Typ.
Guvante
6

Der Nachteil der Typlöschung besteht darin, dass Sie zur Laufzeit den Typ des Generikums nicht kennen. Dies bedeutet, dass Sie keine Reflexion auf sie anwenden und sie zur Laufzeit nicht instanziieren können.

In Java können Sie so etwas nicht tun:

public class MyClass<T> {
    private T t;

    //more methods    
    public void myMethod() {
        //more code
        if (someCondition) {
            t = new T();//illegal
        } else {
            T[] array = new T[];//illegal
        }            
    }       
}

Hierfür gibt es eine Problemumgehung, für die jedoch mehr Code erforderlich ist. Der Vorteil ist, wie Sie bereits erwähnt haben, die Abwärtskompatibilität.

m3th0dman
quelle
3

Ein weiterer Vorteil von gelöschten Generika besteht darin, dass verschiedene Sprachen, die mit der JVM kompiliert werden, unterschiedliche Strategien für Generika verwenden, z. B. Scalas Definitionssite gegenüber Javas Use-Site-Kovarianz. Auch Scalas höherwertige Generika wären bei den reifizierten Typen von .Net schwieriger zu unterstützen gewesen, da Scala bei .Net im Wesentlichen das inkompatible Reifizierungsformat von C # ignorierte. Wenn wir Generika in der JVM reifiziert hätten, wären diese reifizierten Generika höchstwahrscheinlich nicht für die Funktionen geeignet, die wir an Scala wirklich mögen, und wir wären mit etwas Suboptimalem festgefahren. Zitat aus Ola Binis Blog ,

Dies alles bedeutet, dass Sie, wenn Sie der JVM reifizierte Generika hinzufügen möchten, sehr sicher sein sollten, dass diese Implementierung sowohl alle statischen Sprachen umfassen kann, die Innovationen in ihrer eigenen Version von Generika durchführen möchten, als auch alle dynamischen Sprachen, die dies möchten Erstellen Sie eine gute Implementierung und eine schöne Schnittstelle zu Java-Bibliotheken. Wenn Sie reified Generics hinzufügen, die diese Kriterien nicht erfüllen, werden Sie Innovationen ersticken und die Verwendung der JVM als mehrsprachige VM erheblich erschweren.

Persönlich halte ich die Notwendigkeit, einen TypeTagin Scala gebundenen Kontext für widersprüchliche überladene Methoden zu verwenden, nicht für einen Nachteil, da dadurch der Aufwand und die Unflexibilität der Reifizierung von einem globalen (gesamtes Programm und alle möglichen Sprachen) auf eine Verwendungssite pro Sprache verlagert werden Problem, das nur seltener der Fall ist.

Shelby Moore III
quelle
Die Logik von nuttycom, warum reifizierte Generika böse sind .
Shelby Moore III
Ein weiterer Grund für die Bevorzugung gelöschter Generika besteht darin, dass Abhängigkeiten so eingefügt werden sollten, dass die Lösung des Ausdrucksproblems berücksichtigt wird. Wenn Sie in Ihrer generischen Funktion auf bestimmte Fälle von Typen testen oder eine Factory fest codieren, ist die Erweiterbarkeit falsch.
Shelby Moore III