2012-07-18 11 views
7

Okay, so Java erlaubt es nicht, die folgenden:Generics und Wildcards: Java mag "new Foo <Bar<?>>"

Foo<?> hello = new Foo<?>(); 

Dies macht sense-- nach allem, was der Punkt von Generika ist, wenn Sie werde ich sowieso alles boxen/auspacken?

Was seltsam ist, ist Java hat dies zulassen:

Foo<Bar<?>> howdy = new Foo<Bar<?>>(); 

Zugegeben, erreicht dies tatsächlich mehr, wenn auch an einem gewissen Punkt, gäbe es eine Besetzung zu bekommen, was Bar mit arbeitet. Aber wenn Java mit in Ordnung ist, einig Spezifität, warum es nicht zulassen ?:

Foo<? extends Mal> bonjour = new Foo<? extends Mal>(); 

Der einzige Grund, warum ich frage ist, ich bin Befestigung innerhalb einer Klasse Parameter eines Konstruktors auf dem „Wildcard angewiesen "und möchte ernsthaft die Implikationen/Absicht dahinter kennen.

EDIT: Um meine Frage zu klären, aus welchen Gründen sind diese Aussagen erlaubt/nicht erlaubt? Ich bin mir bewusst, dass "Java Wildcards in Konstruktoren nicht erlaubt", aber die Frage ist, warum all diese Seltsamkeit? Warum sind keine beschränkten Platzhalter zulässig, wenn geschachtelte Platzhalter zulässig sind?

Antwort

5

Wie für die Begründung: new Foo<?> sollte wahrscheinlich besser geschrieben werden als new Foo<Object>, so die Compiler-Einschränkung hier, um den Code so lesbar wie möglich zu schreiben. Das letzte Beispiel könnte ebenfalls new Foo<Mak>() lauten, da es nichts gibt, was Sie an einer Foo<? extends Mal> tun können, die Sie auf einem Foo<Mal> nicht tun können.Beachten Sie, dass die Umkehrung nicht wahr ist: Ein Foo<Mal> könnte Mal Argumente akzeptieren, wo ein Foo<? extends Mal> nicht.

Auf der anderen Seite möchten Sie wirklich ein Foo Objekt, das Bar Objekte aller Art behandeln kann, so macht Foo<Bar<?>> macht Sinn. Dies ist der Fall, wenn Sie nur auf die Methoden Bar zugreifen, die nicht auf das Argument type angewiesen sind. Es gibt nichts, worüber sich der Compiler hier beschweren könnte.

+0

Ich glaube, ich verstehe, was Sie sagen: Wenn ein Konstruktor ein Klassenparam 'Mal' akzeptiert, akzeptiert die Instanz grundsätzlich alles, was eine Unterklasse von' Mal' ist (obwohl eine _reference_ auf die Instanz nicht auf a zeigen könnte generic mit einer Unterklasse, es sei denn, ein Platzhalter wurde angegeben.) – Philip

+0

Wenn ich ein Jahr später auf meinen Kommentar schaue ... Ich bin mir ziemlich sicher, dass das eines der unverständlichsten Dinge ist, die ich je gesagt habe. – Philip

1

Wenn Sie eine Instanz eines parametrisierten Typs instanziieren, müssen Sie einen Typ angeben. ? und ? extends Mal sind keine Typen. Bar<?> ist ein Typ. Sie können weitere Informationen zu diesem finden. Hier

ist ein Beispiel für die zweite Fall:

Bar<Object> bar1 = ...; 
Bar<String> bar2 = ...; 
List<Bar<?>> list = new ArrayList<Bar<?>>(); 
list.add(bar1); 
list.add(bar2); 

Sie können sich vorstellen, in einer Liste Bar Instanzen zu speichern, aber Sie brauchen nicht ihre Art Parameter kennen.

3

Der Grund Ihre erste und dritte Erklärungen ist nicht funktionieren wegen JLS §15.9:

Es ist ein Fehler bei der Kompilierung ist, wenn eine der Typargumente in einer Klasseninstanz Erstellung Ausdruck verwendet Wildcard-Typ sind Argumente (§4.5.1).

In Ihrer ersten Deklaration ist ? ein Platzhalter. In Ihrer dritten Deklaration ist ? extends Mal ein Platzhalter-Typ.

Der Grund, warum Ihre zweite Deklaration funktioniert Arbeit ist, weil Bar<?> kein Wildcard-Typ ist. Bar<?> ist der Typ Bar.

Was meinen Sie mit dem "Platzhalter in einem Klassenparameter eines Konstruktors"?

+0

Nun ja, sie sind Wildcards und Platzhalter sind in diesen Fällen nicht erlaubt - aber meine Frage ist, _why_? Warum wurde diese Designentscheidung getroffen? Warum ist der dritte Codeblock weniger legitim als der zweite? – Philip

+0

Damit meine ich etwas in Form von 'New Foo >' – Philip

+0

@Phillip Die ersten und dritten Codebausteine ​​sind ziemlich gleich. Zum ersten Mal könnte das '?' Durch 'Object',' Mal', 'SubclassOfMal',' SubSubclassOfMal' usw. ersetzt werden. Für die dritte könnte das "?" Durch irgendeines der obigen ersetzt werden, außer für "Object". Es gibt immer noch eine unendliche Anzahl von tatsächlichen Typen, die mit dem '?' Übereinstimmen können, und der Compiler kann das nicht verarbeiten. – Jeffrey

1

new Foo<?> kann äquivalent durch new Foo<SomeRandomTypeIMadeUp> ersetzt werden, wobei SomeRandomTypeIMadeUp ein beliebiger Typ ist, der die Grenze dieses Typparameters erfüllt. Die einfachste Wahl besteht darin, einfach die obere Grenze dieses Typparameters auszuwählen, z. Wenn es class Foo<T extends X> ist, dann würde new Foo<X> ausreichen.

Sie können fragen, warum kann ich nur irgendeinen beliebigen Typparameter wählen, sogar einen, der absolut keine Verbindung mit dem Rest meines Programms hat? Ist das nicht unsicher? Die Antwort ist nein, weil genau das Foo<?> bedeutet - der Typparameter kann alles sein, und Sie können nicht davon abhängen, was es ist. Dies zeigt die Absurdität dessen, was Sie tun möchten. Etwas, das mit new Foo<?> erstellt wurde, wäre so gut wie völlig nutzlos, weil Sie damit nichts anfangen können, was vom Typ des Typparameters abhängt.

Typen mit Platzhaltern sind im Allgemeinen nützlich. Zum Beispiel können Sie ein Argument des Typs List<?> haben und Sie können jeden beliebigen Typ von List daran übergeben, und es wird einfach Dinge aus der Liste entfernt. In diesem Fall erstellen Sie die Liste nicht. Die Funktion, mit der die Liste erstellt und an Sie übergeben wurde, hatte wahrscheinlich einen anderen Parameter als den Wildcard-Typ. Im Rahmen dieser Funktion können Sie Dinge in die Liste einfügen und nützliche Dinge damit tun. Wenn eine Funktion ein List<?> erstellen würde; das wäre ziemlich nutzlos - du kannst kein Element außer null darin einfügen.

Dies ist, warum Sie nicht tun dürfen new Foo<?>: Es ist völlig nutzlos; Sie verwenden wahrscheinlich Generics falsch, wenn Sie es verwenden möchten. Und in dem extrem seltenen Fall, dass Sie es wirklich wollen, gibt es einen fertigen Ersatz, new Foo<AnyTypeThatSatisfiesTheBounds>.

Foo<Bar<?>> ist sehr unterschiedlich. Bar<?> ist ein spezifischer Typ. Foo<Bar<?>> bedeutet nicht, dass Sie ihm Foo<Bar<Something>> zuweisen können; eher, das ist illegal; Die Typparameter Foo müssen übereinstimmen, wenn sie keine Platzhalter sind. Auch im Gegensatz zu einem Platzhalter können Sie mit einem List<Bar<?>> Objekte hineinlegen und Objekte daraus entfernen.

Verwandte Themen