103 Stimmen

Warum sollte es mich interessieren, dass Java keine verifizierten Generika hat?

Dies wurde kürzlich in einem Interview als Frage gestellt, die ich dem Kandidaten gestellt habe und die er sich für die Java-Sprache gewünscht hätte. Es ist allgemein bekannt, dass es als störend empfunden wird, dass Java keine reifizierten Generika hat, aber als ich genauer nachfragte, konnte der Kandidat mir eigentlich nicht sagen, welche Art von Dingen er hätte erreichen können, wären sie vorhanden.

Offensichtlich können aufgrund der zulässigen Raw-Typen in Java (und unsicherer Überprüfungen) die Generika umgangen werden und es kann sich so ergeben, dass eine List (zum Beispiel) tatsächlich Strings enthält. Dies könnte offensichtlich unmöglich gemacht werden, wenn Typinformationen reif wären; aber es muss mehr geben als das!

Könnten Personen Beispiele posten von Dingen, die sie wirklich tun würden, wenn reifizierte Generika verfügbar wären? Ich meine, offensichtlich könnten Sie den Typ einer List zur Laufzeit erhalten - aber was würden Sie damit tun?

public  void foo(List l) {
   if (l.getGenericType() == Integer.class) {
       //yeah baby! err, what now?

BEARBEITEN: Ein schnelles Update zu diesem Thema, da die Antworten hauptsächlich darauf abzielen, dass die Notwendigkeit besteht, eine Class als Parameter zu übergeben (zum Beispiel EnumSet.noneOf(TimeUnit.class)). Mir ging es mehr darum, etwas in der Art aufzuzeigen wo dies einfach nicht möglich ist. Zum Beispiel:

List l1 = api.gimmeAList();
List l2 = api.gimmeAnotherList();

if (l1.getGenericType().isAssignableFrom(l2.getGenericType())) {
    l1.addAll(l2); //warum um alles in der Welt würde ich das überhaupt tun?

101voto

RHSeeger Punkte 15604

Das, was mich am häufigsten beißt, ist die Unfähigkeit, von Mehrfachverteilungen über mehrere generische Typen hinweg Gebrauch zu machen. Das Folgende ist nicht möglich und es gibt viele Fälle, in denen es die beste Lösung wäre:

public void my_method(List input) { ... }
public void my_method(List input) { ... }

81voto

BalusC Punkte 1034465

Aus den wenigen Malen, in denen ich auf dieses "Bedürfnis" gestoßen bin, reduziert es sich letztendlich auf diesen Aufbau:

public class Foo {

    private T t;

    public Foo() {
        this.t = new T(); // Hilfe?
    }

}

In C# funktioniert dies, vorausgesetzt, dass T einen Standard-Konstruktor hat. Sie können sogar den Laufzeittyp durch typeof(T) erhalten und die Konstruktoren durch Type.GetConstructor() erhalten.

Die übliche Lösung in Java wäre, Class als Argument zu übergeben.

public class Foo {

    private T t;

    public Foo(Class cls) throws Exception {
        this.t = cls.newInstance();
    }

}

(es muss nicht unbedingt als Konstruktorargument übergeben werden, als Methodenargument ist ebenfalls in Ordnung, das obige ist nur ein Beispiel, auch die try-catch-Klausel wurde der Kürze halber weggelassen)

Für alle anderen generischen Typkonstrukte kann der tatsächliche Typ leicht mit Hilfe von Reflektion aufgelöst werden. Die folgenden Fragen und Antworten veranschaulichen die Anwendungsfälle und Möglichkeiten:

35voto

gustafc Punkte 27673

Typsicherheit kommt in den Sinn. Die Herabstufung auf einen parametrisierten Typ wird immer unsicher sein, ohne reifizierten Generics:

List meineFreunde = new ArrayList();
meineFreunde.add("Alice");
getSession().put("Freunde", meineFreunde);
// später, anderswo
List meineFreunde = (List) getSession().get("Freunde");
meineFreunde.add(new Freund("Bob")); // funktioniert wie ein Zauber!
// und so weiter...
List meineFreunde = (List) getSession().get("Freunde");
for (String freund : meineFreunde) drucken(freund); // ClassCastException, wtf!? 

Außerdem weniger Abstraktionen würden durchsickern - zumindest diejenigen, die möglicherweise an Laufzeitinformationen über ihre Typparameter interessiert sind. Heutzutage, wenn Sie irgendwelche Arten von Laufzeitinformationen über den Typ eines der generischen Parameter benötigen, müssen Sie auch seine Klasse übergeben. Auf diese Weise hängt Ihre externe Schnittstelle von Ihrer Implementierung ab (ob Sie RTTI über Ihre Parameter verwenden oder nicht).

27voto

Turnor Punkte 1816

Sie könnten generische Arrays in Ihrem Code erstellen.

public  static void DoStuff() {
    T[] myArray = new T[42]; // Geht nicht
}

17voto

user2684301 Punkte 2480

Dies ist eine alte Frage, es gibt eine Menge Antworten, aber ich denke, dass die vorhandenen Antworten daneben liegen.

"reified" bedeutet einfach real und bedeutet normalerweise nur das Gegenteil von Typauslöschung.

Das große Problem im Zusammenhang mit Java Generics:

  • Dieses schreckliche Boxing-Erfordernis und die Diskrepanz zwischen Primitiv- und Referenztypen. Dies hängt nicht direkt mit Reifikation oder Typauslöschung zusammen. C#/Scala beheben das.
  • Keine Selbstantypen. JavaFX 8 musste "Builder" aus diesem Grund entfernen. Hat absolut nichts mit Typauslöschung zu tun. Scala behebt das, bin mir nicht sicher bei C#.
  • Keine Deklarationsseiten-Typvarianz. C# 4.0/Scala haben das. Hat absolut nichts mit Typauslöschung zu tun.
  • Kann keine void method(List l) und method(List **l)** überladen. Dies liegt an der Typauslöschung, ist aber äußerst kleinlich. * Keine Unterstützung für Laufzeittypreflexion. Dies ist das Herz der Typauslöschung. Wenn Sie super fortgeschrittene Compiler mögen, die so viel wie möglich Ihrer Programmlogik zur Kompilierzeit überprüfen und beweisen, sollten Sie Reflektion so wenig wie möglich verwenden und diese Art von Typauslöschung sollte Sie nicht stören. Wenn Sie eher patchy, scripty, dynamische Typprogrammierung mögen und es Ihnen nicht so wichtig ist, dass ein Compiler so viel wie möglich Ihrer Logik als korrekt nachweist, dann möchten Sie eine bessere Reflektion und die Behebung von Typauslöschung ist wichtig.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X