Angenommen, ich verwende eine Schnittstelle mit einem generischen Typparameter
%Vor% Die Absicht ist, dass der Typ T
abstrakt ist: Er erzwingt eine Typenzwangsbeschränkung für Implementierungen von Foo
, aber der Client-Code interessiert sich nicht genau für T
.
Dies ist im Zusammenhang mit einer generischen Methode kein Problem:
%Vor% Aber angenommen, ich möchte die Arbeit von doStuff
auflösen und einen Zustand in einer Klasse Bar
speichern. In diesem Fall muss ich den Typ-Parameter Foo
zu Bar
hinzufügen.
Das ist irgendwie komisch, da der Typparameter T
nicht in der öffentlichen Schnittstelle von Bar
erscheint (d. h. er ist in keinem Methodenparameter oder Rückgabetyp enthalten). Gibt es eine Möglichkeit, " T
away" zu quantifizieren? D. h., Kann ich dafür sorgen, dass der Parameter T
in der Schnittstelle von Bar
ausgeblendet wird, wie im Folgenden?
Ihr Problem ist ähnlich dem, das von einem "capture helper" behoben wurde, > aber ich bin mir nicht sicher, ob es auf Ihr zweites Beispiel angewendet werden kann, wo zwei separate Methoden verwendet werden. Ihre erste doStuff
Methode könnte definitiv besser als public void doStuff(Foo<?> foo)
geschrieben werden, da sie unabhängig von Foo
type Parameter funktioniert. Dann wäre das "Capture Helper" -Muster nützlich.
Update: Nachdem ich ein wenig herumgebastelt habe und die Idee von Goetz 'Capture-Helfer erweitert habe, kam ich auf diese Idee. Im Inneren sieht es ein wenig unordentlich aus; von außen würden Sie nichts ahnen.
%Vor% Um nützlich zu sein, werden Sie irgendwann das Feld foo
einstellen. Zu diesem Zeitpunkt sollten Sie T
kennen (oder erfassen können). Ich würde vorschlagen, das im Konstruktor zu tun, und dann würde es Sinn machen, dass Bar
einen generischen Parameter hat. Sie können sogar eine Schnittstelle verwenden, damit der Clientcode den Typ nicht sehen muss. Ich nehme jedoch an, dass Sie meinen Rat nicht annehmen und wirklich ein setFoo
wollen. Fügen Sie einfach einen Punkt zur umschaltbaren Implementierung hinzu:
Warum nicht eine dreistufige Hierarchie haben:
%Vor% Kunden wissen nur über Foo
, das keine generischen Methoden enthält. Führen Sie alle generischen Methoden ein, die Sie in FooImplBase<T>
benötigen, und dann wird die konkrete Klasse daraus abgeleitet.
In Ihrem Beispiel wären also startStuff()
und endStuff()
in Foo
abstrakt und in FooImplBase<T>
implementiert. Hört sich das so an, als könnte es in deiner realen Situation funktionieren? Ich stimme zu, es ist ein bisschen umständlich.
Sie definieren die Bar-Klasse. Zwei Dinge sind wahr ...
1) In Bar ist kein parametrischer Typ enthalten. Das heißt, die foo und t Mitglieder haben einen einzigen Typ, zB U, der für die Definition festgelegt ist. Wenn Sie ein Foo übergeben, das Sie foo zuweisen möchten, muss es ein Foo & lt; U & gt; sein. Wenn das alles wahr ist, dann ist es ja nicht Teil der öffentlichen Schnittstelle - alles hat einen bestimmten Typ. Dann bin ich nicht sicher, was Sie mit "quantifizieren" meinen, da es keine Variablen vom Typ gibt. Wenn Sie allgemein quantifizieren wollen, wie können Sie die Tatsache, dass Bar keine parametrische Typisierung hat, in Einklang bringen, also müssen Sie für jeden seiner Mitglieder einen konkreten Typ erhalten haben?
2) In Bar ist ein parametrischer Typ enthalten. Es ist vielleicht nicht offensichtlich in der öffentlichen Schnittstelle, aber vielleicht übergeben Sie ein Foo & lt; T & gt ;, so dass Sie möchten, dass eine Bar-Klasse mit mehr als einem einzigen Typ instanziiert wird. Dann, wie gesagt, dies ist in der öffentlichen Schnittstelle, und Sie müssen Bar in T mit Generika parametrisch machen. Dies gibt Ihnen eine Form der universellen Quantifizierung für die Definition, "Für alle Typen T ist diese Definition wahr".
Irgendwo muss entschieden werden, welchen Typ Sie für "T" innerhalb der Bar-Klasse verwenden möchten. Entweder Sie müssen es in der Definition von Bar wählen (indem Sie innerhalb der Klassendefinition Foo durch Foo ersetzen) oder Sie überlassen es dem Client der Bar-Klasse: In diesem Fall muss Bar generisch gemacht werden.
Wenn Sie möchten, dass eine Schnittstelle zu Bar, die nicht auf T und angewiesen ist, verschiedene Typen für T auswählen kann, sollten Sie eine nicht-generische Schnittstelle oder eine abstrakte Basisklasse verwenden, wie in:
%Vor%Wenn du wirklich etwas malen willst, kannst du die Bar-Klasse in die Foo-Schnittstelle einfügen und das T auf diese Weise saugen. Weitere Informationen zu Klassen in Schnittstellen finden Sie in diesem Artikel . Vielleicht ist das der einzige Fall, wo das Sinn macht?
%Vor%