Ich muss die Methodensignatur eingeben, damit sie 2 gleich typisierte Parameter verschiedener konkreter Subtypen akzeptiert.
Ist es möglich, so etwas mit Generika zu kodieren? Wie würdest du es lösen? (Der Fall ist absolut ein Beispiel)
%Vor%EDIT: Was ich am Ende suche, ist eine Möglichkeit für den Compiler:
%Vor%aber nicht:
%Vor%noch
%Vor%EDIT 2: Ich habe ein besseres Beispiel gefunden. Stellen Sie sich eine Schnittstelle Tuple vor, mit den Kindklassen Duple, Triple & gt; ... wäre es perfekt, etwas wie
zu haben %Vor% Zuerst werden die Generatoren des Methodenarguments los. Es gibt keinen Grund, einen Aufrufer dazu zu zwingen, ArrayList<Integer>
und ArrayList<Boolean>
anzugeben, wenn Sie eine ArrayList<String>
zurückgeben möchten. Akzeptiere einfach List<Integer>
und List<Boolean>
und überlasse es deiner Methode, sie in die entsprechende Rückgabe List
zu verwandeln.
Da Sie wissen, dass Sie eine Art List
von String
zurückgeben möchten, können Sie Ihren Parameter als <T extends List<String>>
und Ihren Rückgabetyp einfach als T
schreiben.
Das lässt uns den schwierigen Teil: Ihre Methode zu instanziieren ein Objekt unbekannten Typs. Das ist schwierig. Du kannst nicht einfach new T();
machen. Sie müssen etwas aufrufen, das in Ihrem Namen ein T
erzeugt. Glücklicherweise bietet Java 8 eine funktionale Schnittstelle für Supplier<T>
. Sie müssen nur die Methode get()
aufrufen, um Ihre ArrayList<String>
oder was auch immer Sie wollen, zu erhalten. Der Teil, der schmerzhaft ist, ist, dass dein Invoker sein eigenes Supplier
bereitstellen muss. Aber ich denke, das ist so gut wie es in Java 8 ist.
Hier ist der Code:
%Vor%Zumindest das kompiliert:
%Vor%Und das nicht:
%Vor%Sie können sogar den Typparameter des Aufrufs weglassen. Dies kompiliert:
%Vor%Und das nicht:
%Vor%Kurz gesagt: Typargumente können nicht selbst parametrisiert werden. Und das liegt daran, dass wir nicht wissen, wie viele Typ-Argumente sie selbst nehmen würden und welche Beschränkungen ihnen auferlegt werden könnten.
Nimm die relativ obskure Klasse RoleList
. Es erweitert ArrayList<Object>
, also passt es List<?>
. Aber es braucht überhaupt kein Typargument. Wenn also jemand Ihre Methode sum()
mit RoleList
aufgerufen hat, würde das in Ihrem Beispiel Folgendes erfordern:
Das kann natürlich nicht funktionieren, da ein typunparametrierter Typ benötigt wird, um Typargumente zu nehmen. Und wenn Sie die Argumente des Typs wie folgt entfernen:
%Vor% Dann muss Ihre Methode in der Lage sein, zwei List<Object>
Argumente zu akzeptieren und einen Wert von List<Object>
zurückzugeben. Und das verletzt deine Grundvoraussetzung, dass du solche Dinge kontrollieren kannst.
In der Praxis sollte RoleList
hier überhaupt nicht erlaubt sein, weil Sie niemals garantieren können, dass eine Instanz nur Integer
s, eine weitere nur Boolean
s und eine dritte nur String
s enthält . Ein Compiler, der RoleList
hier erlaubt, würde notwendigerweise eine schwächere Typprüfung haben als wir jetzt haben.
Das Entscheidende ist, dass Sie einfach nicht das tun können, was Sie fragen, denn Java ist einfach nicht so aufgebaut.
Sie können die vollständige Typsicherheit innerhalb Ihrer sum()
-Methode mit meiner oben vorgeschlagenen Methode erhalten. Sie stellen sicher, dass die eingehenden List
s nur die Werte Integer
bzw. Boolean
enthalten. Sie stellen sicher, dass der Aufrufer sich auf die Rückgabe eines bestimmten Subtyps von List
verlassen kann, der nur String
-Werte enthält. Alle Garantien, die einen Unterschied machen, sind da.
Es gibt zwei Dinge, die mich über das oben genannte auffallen. Wie instanziieren Sie sublistOfStrings
und welche Vorteile erwarten Sie, wenn Sie die einfache alte Vererbung verwenden?
Es gibt mehrere Möglichkeiten, T<String>
zu instanziieren. Sie könnten eine Factory die Klasse Ihrer Argumente überprüfen lassen und basierend darauf instanziieren. Oder Sie könnten etwas tun wie:
Aber Sie können nicht einfach new T<String>()
gehen. Sie gründen also die Implementierung Ihres Rückgabetyps sowieso auf den Typ eines Ihrer Argumente (es sei denn, es gibt eine Möglichkeit, an die ich nicht gedacht habe).
Wenn beide Argumente vom Typ 'T' angegeben werden, heißt das nicht, dass sie genau vom selben konkreten Typ, T 'sind. Zum Beispiel
%Vor% Sie erzwingen also nicht, dass sublistOfInts
und sublistOfBooleans
beide vom Typ say ArrayList
sind und Sie daher ArrayList
zurückgeben können. Sie müssen weiterhin Code schreiben, um zu überprüfen, welcher List<?>
-Typ basierend auf den Argumenten zurückgegeben werden soll.
Ich denke, es ist besser, keine Generika zu verwenden und so etwas zu verwenden:
%Vor% Sie können es immer noch mit Untertypen von List<?>
aufrufen. Ich glaube nicht, dass es einen Vorteil gibt, den man von Generika bekommen könnte, selbst wenn Java es erlaubt hätte (was nicht der Fall ist, weil T
nicht so parametrisiert werden kann).
Ich weiß, was Sie haben, ist nur ein Beispiel, aber wenn Sie nur eine einzige Liste zurückgeben wollen, die den String-Wert aller Inhalte in einer Gruppe anderer Listen enthält, können Sie einfach eine Methode angeben, die eine Reihe von unbegrenzten Listen enthält .
%Vor%