Wie auf ein generischer Rückgabetyp mit mehreren Begrenzungen
Ich habe vor kurzem gesehen, dass man erklären können, ein return-Typ, der ist auch begrenzt, die durch ein interface. Betrachten Sie die folgenden Klassen und Schnittstellen:
public class Foo {
public String getFoo() { ... }
}
public interface Bar {
public void setBar(String bar);
}
Kann ich erklären, eine Rückkehr geben wie diese:
public class FooBar {
public static <T extends Foo & Bar> T getFooBar() {
//some implementation that returns a Foo object,
//which is forced to implement Bar
}
}
Wenn ich den Anruf, dass die Methode von irgendwo, meine IDE sagt mir, dass der Rückgabetyp hat die Methode String getFoo()
sowie setBar(String)
, aber nur, Wenn ich Punkt ein Punkt hinter der Funktion wie diese:
FooBar.getFooBar(). //here the IDE is showing the available methods.
Gibt es eine Möglichkeit, um einen Verweis auf ein solches Objekt? Ich meine, wenn ich das machen würde, so etwas wie dieses:
//bar only has the method setBar(String)
Bar bar = FooBar.getFooBar();
//foo only has the getFoo():String method
Foo foo = FooBar.getFooBar();
Ich würde gerne eine Referenz wie folgt aus (pseudo-code):
<T extents Foo & Bar> fooBar = FooBar.getFooBar();
//or maybe
$1Bar bar = FooBar.getFooBar();
//or else maybe
Foo&Bar bar = FooBar.getFooBar();
Ist dies irgendwie möglich in Java, oder bin ich nur in der Lage zu erklären, Rückgabe-Typen wie diesem? Ich glaube Java hat, geben Sie es auch, irgendwie. Ich würde es vorziehen, nicht zu greifen, um einen wrapper, wie es fühlt sich an wie Cheaten:
public class FooBarWrapper<T extends Foo&Bar> extends Foo implements Bar {
public T foobar;
public TestClass(T val){
foobar = val;
}
@Override
public void setBar(String bar) {
foobar.setBar(bar);
}
@Override
public String getFoo() {
return foobar.getFoo();
}
}
Habe Java wirklich zu erfinden, so ein nettes feature, aber vergessen, dass man gerne hätte, eine Referenz auf sich?
- "Ich möchte einen Zeiger wie diese" - das können Sie nicht. Variablen haben einen bekannten (evtl. gelöscht) - Typ und einen Typ-Einschränkung ist nicht ein Typ
- Außerdem, kann man eigentlich nicht alles zurückzugeben, was von
getFooBar()
ohne casting es um (T), das sollte einen Hinweis darauf geben, dass Sie etwas falsch machen. - (Seltsam genug, dieser entdeckt einen Fehler in IntelliJ code-Analyse-engine, es erkennt nur die Methoden der
Foo
als gültig.) - Könnte die downvoter bitte erklären? Dies ist eine verständliche und relativ gut geschrieben Frage.
- Nur um zu verdeutlichen, dass die bounty Nachricht, ich bin speziell auf der Suche nach einem Anwendungsfall für eine syntax wie
Foo&Bar bothFooAndBar = ...
- mitFoo&Bar
als Typ einer variable (wie die Frage fragt nach) oder eine Rückkehr geben.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Während der Typ-Parameter von generischen Methode kann eingeschränkt werden, indem Grenzen, wie
extends Foo & Bar
sind Sie letztendlich entscheiden die Anrufer. Wenn Sie anrufengetFooBar()
, der Aufruf der Seite schon weiß, wasT
wird aufgelöst. Oft, diese Art von Parametern wird abgeleitet durch den compiler, weshalb Sie nicht in der Regel müssen Sie angeben, wie diese:Aber selbst wenn
T
abgeleitet werdenFooAndBar
, das ist wirklich, was passiert hinter den kulissen.Also, um Ihre Frage zu beantworten, wie eine syntax wie diese:
Würde nie in der Praxis. Der Grund dafür ist, dass der Anrufer muss schon wissen was
T
ist. EntwederT
ist einige konkrete Typ:Oder
T
ist eine ungelöste parameter "type" und wir sind in seinem Geltungsbereich:Anderes Beispiel:
Technisch, das hüllt die Antwort. Aber ich möchte auch darauf hinweisen, dass dein Beispiel-Methode
getFooBar
ist inhärent unsicher. Denken Sie daran, dass der Anrufer entscheidet, wasT
bekommt, nicht die Methode. DagetFooBar
nimmt keine Parameter in Bezug aufT
, und weil der type erasure, Ihre einzigen Optionen sind, wäre die Rückkehrnull
oder zu "Lügen", indem Sie eine unchecked cast, riskieren heap pollution. Ein typischer workaround wäre fürgetFooBar
zu nehmenClass<T>
argument, oder sonst eineFooFactory<T>
zum Beispiel.Update
Es stellt sich heraus, ich war falsch, wenn ich behauptete, dass der Anrufer von
getFooBar
muss immer wissen, wasT
ist. Wie @MiserableVariable Punkte heraus, gibt es einige Situationen, in denen das typargument einer generischen Methode abgeleitet, um eine wildcard capture, statt einen konkreten Typ oder Typ-variable. Sehen seine Antwort für ein großartiges Beispiel für einegetFooBar
Umsetzung, dass ein proxy verwendet wird, nach Hause zu fahren sein Punkt, dassT
ist unbekannt.Wie wir diskutiert in den Kommentaren, ein Beispiel mit
getFooBar
erzeugt Verwirrung, da braucht es keine Argumente, um zu folgernT
aus. Bestimmte Compiler wirft einen Fehler auf einem zusammenhangslosen AufrufgetFooBar()
während andere sind gut mit dabei. Ich dachte, dass die inkonsistente Fehler bei der Kompilierung zusammen mit der Tatsache, dass der AufrufFooBar.<?>getFooBar()
illegal ist - bestätigt meinen Punkt, aber dieser entpuppte sich als Ablenkungsmanöver.Basierend auf @MiserableVariable Antwort, ich habe ein neues Beispiel verwendet eine generische Methode mit einem argument zu entfernen, die Verwirrung. Angenommen, wir haben Schnittstellen
Foo
undBar
und eine ImplementierungFooBarImpl
:Wir haben auch eine einfache container-Klasse, umschließt eine Instanz eines Typs Umsetzung
Foo
undBar
. Es erklärt eine dumme statische Methodeunwrap
nimmtFooBarContainer
und gibt Ihr referent:Nun sagen wir, wir haben ein wildcard-parametrisierten Typ von
FooBarContainer
:Dürfen wir passieren
unknownFooBarContainer
inunwrap
. Dies zeigt meiner früheren Behauptung war falsch, denn der call-site nicht weiß, wasT
ist - nur, dass es einige geben innerhalb der Grenzenextends Foo & Bar
.Als ich merkte, ruft
unwrap
mit einer wildcard-ist auch illegal:Ich kann nur vermuten, dass dies ist, weil wildcard erfasst, kann nie mit jeder anderen - die
?
argument an der call-site ist mehrdeutig, mit keine Möglichkeit zu sagen, dass sollte es insbesondere entsprechen die Platzhalter in der Art vonunknownFooBarContainer
.So, hier ist der use case für die syntax der OP ist gefragt. Aufruf
unwrap
aufunknownFooBarContainer
liefert eine Referenz vom Typ? extends Foo & Bar
. Wir können ordnen, dass der Verweis aufFoo
oderBar
, aber nicht beides:Wenn aus irgendeinem Grund
unwrap
waren teuer und wir wollten nur rufen Sie es einmal, wir wären gezwungen, Darsteller:So ist dies, wo die hypothetische syntax würde in handliches kommen:
Dies ist nur eines ziemlich obskuren use-case. Es wäre ziemlich weitreichende Konsequenzen für die eine solche syntax, sowohl gute als auch schlechte. Es eröffnet sich Raum für Missbrauch, wo Sie nicht benötigt, und es ist völlig verständlich, warum die Sprache-Designer nicht umsetzen ist so eine Sache. Aber ich denke immer noch, es ist interessant zu denken.
Ein Hinweis zur heap pollution
(Meist für @MiserableVariable) Hier eine Exemplarische Vorgehensweise, wie eine unsichere Methode, wie
getFooBar
verursacht heap-Luftverschmutzung, und deren Auswirkungen. Die folgende Schnittstelle und Implementierungen:Let ' s implementieren eine unsichere Methode
getFoo
ähnlichgetFooBar
aber vereinfacht für dieses Beispiel:Hier, wenn der neue
Foo2
geworfen zuT
, es ist "nicht aktiviert", was bedeutet, da der type erasure die Laufzeit der es nicht kennt, sollte scheitern, obwohl es in diesem Fall, daT
warFoo1
. Anstelle der heap ist "verunreinigt", was bedeutet, Referenzen auf Objekte, die Sie sollten nicht erlaubt gewesen.Den Ausfall geschieht nach der Methode zurückgegeben wird, wenn die
Foo2
Instanz versucht zugewiesenfoo1
Referenz, die die reifiable TypFoo1
.Sind Sie wahrscheinlich denken, "Okay, so explodierte es an der call-site, anstatt die Methode, große Sache." Aber es kann leicht komplizierter, wenn mehr Generika beteiligt sind. Zum Beispiel:
Nun ist es nicht sprengen, auf die call-Seite. Es weht bis irgendwann später, wenn sich der Inhalt
foo1List
gewöhnen. Dies ist, wie heap Umweltverschmutzung wird immer schwerer zu Debuggen, da das exception-stacktrace nicht Sie das eigentliche problem.Wird es sogar noch komplizierter, wenn der Anrufer im generischen Bereich selbst. Stellen Sie sich anstelle einer
List<Foo1>
bekommen wirList<T>
, indem es in eineMap<K, List<T>>
und wieder noch eine andere Methode. Sie erhalten die Idee, wie ich hoffe.static <U extends Foo & Bar> processFooBar(U u)
ich nenne es direkt alsprocessFooBar(getFooBar())
. Ich habe nicht versucht, aber ich vermute, die bekommen können Sie zurück ein proxy implementiert, dass beideFoo
undBar
(ich rekodiert die OP ' s Beispiel etwas) das heißt, es kann eigentlich nicht sein, einen Namen geben erfüllt, dass beide Einschränkungen. Also ich verstehe nicht, warum Sie sagen, dass der Aufrufer muss wissen, was T istT
. Es kann eine andere Methode aufgerufen, die auf den zurückgegebenen Wert und zuweisen können, um Sie entweder einenFoo
oderBar
Hinweis, aber nicht beide. Nicht, die scheinen ein syntax-Loch?getFooBar()
TypFooBar
ist nicht anwendbar für die Argumente()
. Der abgeleitete TypFoo
ist kein Gültiger Ersatz für die begrenzt parameter<T extends Foo & Bar>
". So ohne Kontext wie diesem Eclipse ist die Herleitung der Löschung vonT
fürT
. Kann nicht sagen, was javac ist abzuleiten fürT
wenn überhaupt - nicht herausfinden können, wie man NetBeans zu mir sagen.T
sollte standardmäßig zu seiner LöschungFoo
wenn es kann nicht abgeleitet werden aus den Argumenten oder Zuordnung (dies geschieht Häufig mit verschachtelten generischen Aufrufe) - in anderen Worten, ich bin Abstellgleis mit Eclipse, von dem bekannt ist, werden mehr streng richtig, als javac in einigen Generika-Situationen. Sollte es nicht möglich sein, fürT
zu unbekannt sein, da kann man nicht nennen eine generische Methode mit Platzhalter-Typ-Argumente, z.B.FooBar.<?>getFooBar()
.Foo
wenn die Einschränkung istextends Foo & Bar
. FWIW Eclipse kompiliert und läuft es bei mir mit 1,6-compliance nur in Ordnung, außer für die unchecked-Warnungen.new T()
in Java. SogetFooBar
müssen einige HerstellerT
.Class<T>
ist nur ein Beispiel - es hat dienewInstance
Verknüpfung undcast
können Sie auch tun, eine checked cast. Nicht sicher, was du sagst über die Platzhalter aber.Class<T>
Token, die Ihnen helfen, um Typ-Verunreinigung ist derClass.cast()
Methode. Sie sind im Grunde ersetzen die compile-Zeit werfen, umT
was unsicher ist (und kann nicht sicher gemacht werden) mit einer Laufzeit überprüfen. Dieser bewegt sich überprüfen Art Konsistenz der call-Seite, wo der compiler sicherstellen kann, dass der Typ-parameter der Methode und derClass
übereinstimmen. Die Frage "how to get rund um heap pollution ohneClass<T>
?" nicht wirklich Sinn macht, denn das ist der vorgesehene Weg, um das zu verhindern. (Die Methode ist neu in 1.5) Es ist wie zu Fragen "wie, um Variablen zu deklarieren, ohne keywords?".T
ist - schauen Sie sich dieses Beispiel, das ich zusammengestellt habe, basierend auf dem code, den Sie geteilt: ideone.com/V1VED9. Ich denke, die früherengetFooBar
Beispiel war eine seltsame Ecke Fall, weil Sie nicht über die Argumente zu entnehmen, so dass einige Compiler wurden Würgen auf Sie und warf mich ab. Das neue Beispiel zeigt deutlich, dass?
abgeleitet werden kann (aber nicht spezifiziert). Wie gezeigt, diese bietet einen schönen use-case für die hypothetischeFoo&Bar fooBar = ...
syntax.Gibt es Fälle, wo eine aufgerufene Methode gibt einen Wert zurück, kann der Anrufer ohne Kenntnis der konkreten Typ. Es ist sogar wahrscheinlich, dass eine solche überhaupt nicht existiert, es ist nur ein proxy:
Beide
FooBar1
undFooBar2
implementierenFoo
undBar
. Inmain
, die Anrufe zugetFooBar1
undgetFooBar2
kann einer Variablen zugewiesen werden, obwohl es ist nicht ein starker Grund für Sie, zu wissen, IMHO.Aber
getFooBar
ist der interessante Fall, die ein proxy verwendet wird. In der Praxis kann es sein das nur Instanz von einem Objekt implementiert die zwei Schnittstellen. Eine andere Methode (show
hier) kann verwendet werden, mit einer temporären in eine Typ-sicherer Art und Weise, aber es kann nicht einer Variablen zugewiesen werden, ohne dieFooBarWrapper
hack in der Frage beschriebenen. Es ist auch nicht möglich, erstellen Sie eine generische wrapperclass Wrapper<T extends U & V>
ist nicht erlaubt.Das einzige Problem scheint zu sein das definieren einer syntax, andere Art Kontrollmechanismen scheinen, um im Platz zu sein, zumindest in Oracle javac 1.7.0.
Wie @Paul Bellora erwähnt in seiner Antwort, der Typ aufgelöst werden, indem der Anrufer, da im wesentlichen wird es jetzt, was es ist Berufung. Ich möchte nur hinzufügen, um seine Antwort mit einem Anwendungsfall, in dem ich denke, dass die Verwendung der syntax von nutzen sein konnte.
Gibt es immer alternativen, vermeiden Sie die Verwendung solcher syntax. Ich kann mir nicht vorstellen an einem einzigen Beispiel, dass dies absolut notwendig ist. Aber ich kann mir einen Einsatz bei einer bestimmten situation, dass diese syntax verwendet werden könnte, günstig, obwohl ich nicht einmal es mir. Ich weiß, das ist nicht das beste Beispiel, aber es kann auf den Punkt kommen.
Fall
Kurzem arbeite ich in der Entwicklung einer Benutzeroberfläche. In dieser Anwendung verwende ich eine Bibliothek zur Verwaltung der GUI-Elemente. Zusätzlich zu den Funktionen der Bibliothek, erstellte ich eine benutzerdefinierte Schnittstelle, die eine Ansicht definiert, die in meiner Anwendung, die Eingänge für einen bestimmten Typ von Daten, sagen wir, Eingabe von Koordinaten. Das interface Aussehen würde:
Ich mehrere Fenster über meine Anwendung, die diese Schnittstelle implementieren. Jetzt können wir sagen, dass aus irgendeinem Grund, die ich speichern möchten, die in einem Modell der letzten Koordinate vorgelegt, in der ein Fenster, und schließen Sie das Fenster nach rechts. Dazu kann ich anfügen eine Prozedur, um das Fenster button, der das Formular sendet, wird der handler wird ausgelöst, wenn der Benutzer das Fenster schließt. Ich konnte erreichen, dass, indem Sie einfach die handler anonym in jedem Fenster, wie:
Aber dieses design ist nicht wünschenswert, für mich, es ist nicht modular genug. Bedenkt, dass ich haben eine anständige Menge an windows mit diesem Verhalten ändern, könnte es auch ziemlich langweilig. Also ich eher Extrakt der anonymen Methode in einer Klasse, so dass es wäre leichter zu ändern und zu pflegen. Aber das problem ist, dass die destroy() Methode ist nicht definiert eine Schnittstelle, ist nur ein Teil der Fenster und die getCoordinate () - Methode definiert ist, die in der Schnittstelle definierte ich.
Nutzung
In diesem Fall könnte ich mehrere Grenzen, wie die folgenden:
Dann den code in das windows wird jetzt:
Beachten Sie, dass das Verhalten bleibt das selbe, der code ist nur ein zusammenhalt wie früher zu sein. Seine nur mehr modular, aber es nicht erforderlich, die Einführung einer zusätzlichen Schnittstelle, um in der Lage sein, zu extrahieren, es richtig zu stellen.
Alternative
Alternativ hätte ich definiert eine zusätzliche Schnittstelle erweitern
CoordinateView
und eine Methode definieren, um das Fenster zu schließen.Dass die Fenster implementieren diese spezifischer Oberfläche, anstatt unnötige Verwendung von generischen Parameter in der extrahierten controller:
Dieser Ansatz für einige gesehen werden kann, wie viel sauberer als die Vorherige und noch mehr wiederverwendbar, da es nun Hinzugefügt werden könnten, um andere "windows" - außerhalb der angegebenen Hierarchie. Ich persönlich bevorzuge diesen Ansatz als gut. Jedoch, kann es in ein wenig mehr codieren, da ein neues interface definiert werden, nur um sich einen Zugang zu einer gewünschten Methode.
Im Abschluss, obwohl ich persönlich es nicht empfehlen, ich denke, mit generischen Typen mit mehreren Grenzen könnten helfen, Kupplung Definitionen, während die Verringerung der Menge an code.
Foo&Bar bothFooAndBar = ...
wie in mitFoo&Bar
als Typ einer variable (wie die Frage fragt nach) eher als Allgemeine Grenzen. Sorry, ich sollte gemacht haben, dass mehr klar. +1 für ein großartiges Beispiel der Verwendung von mehreren generischen Grenzen obwohl.Nicht sicher, was Eclipse tut für Sie, aber die meisten der code oben nicht nahe kommen zu kompilieren....
Ich die entsprechenden änderungen vorgenommen, um es zu kompilieren, so viel wie möglich, und hier ist, was ich bekomme: