Java Generics - Implementierung von Funktionen höherer Ordnung wie map

8

Ich habe beschlossen, einige allgemeine Funktionen höherer Ordnung in Java zu schreiben (map, filter, reduce usw.), die typsicher über Generika sind, und ich habe Probleme mit Platzhaltern, die in einer bestimmten Funktion übereinstimmen.

Um zu vervollständigen, ist die Funktor-Schnittstelle das:

%Vor%

Die störende Funktion ist wie eine Karte , aber anstatt eine Sammlung zu transformieren, transformiert sie eine Karte (zuerst dachte ich, dass es so sein sollte) genannt mapMap , aber es hörte sich so dumm an, dass ich es schließlich remapEntries ) nannte.

Meine erste Version war (und setz dich, weil die Signatur ein Monster ist):

%Vor%

Und es scheint recht korrekt zu sein, aber das Problem ist, dass die verwendete Transformation genau mit den Typparametern übereinstimmen muss, was die Wiederverwendung von Kartenfunktionen für kompatible Typen erschwert. Also habe ich beschlossen, der Signatur Platzhalter hinzuzufügen, und das endete folgendermaßen:

%Vor%

Aber wenn ich versuche, es zu testen, scheitert Wildcard-Abgleich:

%Vor%

Der Fehler ist:

%Vor%

Also, irgendwelche Hinweise, wie das zu beheben ist? Oder sollte ich aufgeben und explizite Schleifen dafür schreiben? ^ _ ^

    
fortran 26.01.2011, 10:49
quelle

3 Antworten

5

Ich denke, Sie sollten sich die Google Guava-API ansehen.

>

Dort finden Sie eine Funktion -Schnittstelle ähnlich wie bei deiner Transformation. Es gibt auch eine Klasse Maps mit Hilfsmethoden zu Erstellen oder Transformieren von Karteninstanzen.

Sie sollten auch PECS berücksichtigen, wenn Sie Methoden für Generika implementieren.

>     
hhbarriuso 26.01.2011, 12:03
quelle
5

Das ist schwierig. Das folgende Wissen ist völlig nutzlos und niemand sollte sich darum kümmern, zu besitzen:

Als erstes muss der Typ von swap behoben werden. Der Eingabetyp sollte nicht Entry<String,Number> sein, da dann Entry<String,Integer> nicht akzeptiert werden kann, was kein Untertyp von E<S,N> ist. % Co_de% ist jedoch ein Untertyp von E<S,I> . Unser Transformator sollte das als Input nehmen. Für die Ausgabe keine Wildcard, da der Transformer nur einen konkreten Typ instanziieren kann. Wir wollen nur ehrlich und genau sein, was konsumiert werden kann und was produziert wird:

%Vor%

Hinweis E<? extends S,? extends N> ist endgültig und niemand erweitert es, aber ich fürchte, das generische System ist nicht so klug, das zu wissen, also habe ich grundsätzlich String auf jeden Fall für später gut gemacht.

Dann denken wir an ? extends String . Wir vermuten, dass die meisten Transformatoren eine ähnliche Typdeklaration wie die remapEntries() haben werden, weil wir die Rechtfertigungen aufgestellt haben. Also haben wir besser

%Vor%

um dieses Argument richtig zu finden. Von dort aus erarbeiten wir die Art der Quelle und des Ergebnisses, wir wollen, dass sie so allgemein wie möglich sind:

%Vor%

swap ist nicht notwendig, es ist in Ordnung, direkt RM zu verwenden. Aber anscheinend möchten Sie, dass der Rückgabetyp identisch mit dem Map<? super RK, ? super RV> -Typ im Kontext des Aufrufers ist. Ich hätte einfach den Rückgabetyp result - es gibt schon genug Probleme.

Dieses Ding wird scheitern, wenn void swap nicht verwendet. Zum Beispiel, wenn der Eingabetyp ? extends ist, ist es lächerlich, String-Integer von ihnen zu machen. Sie können jedoch eine Überladungsmethode mit einer anderen Parametertypdeklaration verwenden, um mit diesem Fall übereinzustimmen.

Ok, das hat funktioniert, aus reinem Glück. Aber es ist total nicht lohnt sich. Ihr Leben ist viel besser, wenn Sie es einfach vergessen, und verwenden Sie Raw-Typ, dokumentieren Sie die Parameter in Englisch, Typ-Typ zur Laufzeit überprüfen. Fragen Sie sich, kauft Ihnen die generische Version irgendetwas? Sehr wenig, bei dem riesigen Preis, den Code völlig unverständlich zu machen. Niemand, auch du selbst und ich, könnte einen Sinn daraus machen, wenn wir morgen früh die Methodensignatur lesen. Es ist viel viel schlimmer als Regex.

    
irreputable 26.01.2011 13:39
quelle
1

Etwas ist mir plötzlich in den Sinn gekommen: Wenn die Platzhalter in verschachtelten generischen Parametern nicht erfasst werden, da sie buchstäblich Teil des Typs sind, könnte ich die umgekehrten Grenzen in den Maps verwenden, anstatt sie in% zu verwenden. co_de%.

%Vor%

Das einzige Problem ist, dass wir die unkontrollierte Umwandlung in Transformation vornehmen müssen. Es wäre vollkommen sicher, wenn die Transformation.apply -Schnittstelle schreibgeschützt wäre, also können wir einfach die Finger kreuzen und hoffen, dass die Umwandlung nicht versucht, Map.Entry aufzurufen.

Wir könnten immer noch einen unveränderlichen Wrapper der Schnittstelle Map.Entry.setValue übergeben, der eine Ausnahme ausgelöst hat, wenn die Methode Map.Entry aufgerufen wurde, um mindestens die Sicherheit vom Typ Runtime sicherzustellen.

Oder mache einfach eine explizite unveränderbare Entry-Schnittstelle und benutze sie, aber das ist ein bisschen wie ein Cheaten (mit zwei verschiedenen Transformationen):

%Vor%     
fortran 26.01.2011 17:24
quelle