Geben Sie einen generischen Typ, aber keinen eigenen Typ in Java Generics ein

8

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%     
Whimusical 29.12.2015, 13:32
quelle

3 Antworten

4

Was ich Ihnen vorschlage, tun Sie stattdessen

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%

Warum kannst du nicht einfach tun, was du verlangst

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:

%Vor%

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.

Warum ist das in Ordnung?

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.

    
Erick G. Hagstrom 29.12.2015, 16:09
quelle
2

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:

%Vor%

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

    
AndyN 29.12.2015 14:30
quelle
0

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%     
George Mulligan 29.12.2015 14:42
quelle

Tags und Links