Ich habe eine generische Schnittstelle
public interface Consumer<E> {
public void consume(E e);
}
Ich habe eine Klasse, die zwei Arten von Objekten verwendet, also möchte ich etwas tun wie:
public class TwoTypesConsumer implements Consumer<Tomato>, Consumer<Apple>
{
public void consume(Tomato t) { ..... }
public void consume(Apple a) { ...... }
}
Anscheinend kann ich das nicht machen.
Ich kann den Versand natürlich selbst durchführen, z
public class TwoTypesConsumer implements Consumer<Object> {
public void consume(Object o) {
if (o instanceof Tomato) { ..... }
else if (o instanceof Apple) { ..... }
else { throw new IllegalArgumentException(...) }
}
}
Ich bin jedoch auf der Suche nach der Lösung für die Typprüfung und den Versand zur Kompilierungszeit, die Generika bieten.
Die beste Lösung, die ich mir vorstellen kann, besteht darin, separate Schnittstellen zu definieren, z
public interface AppleConsumer {
public void consume(Apple a);
}
Funktionell ist diese Lösung in Ordnung, denke ich. Es ist nur wortreich und hässlich.
Irgendwelche Ideen?
java
generics
interface
multiple-inheritance
Daphshez
quelle
quelle
Antworten:
Betrachten Sie die Kapselung:
Wenn Sie das Erstellen dieser statischen inneren Klassen stört, können Sie anonyme Klassen verwenden:
quelle
TwoTypesConsumer
erfüllt keine Verträge, also was ist der Sinn? Es kann nicht an eine Methode übergeben werden, die einen der beiden Typen möchteConsumer
. Die ganze Idee eines Zwei-Typ-Verbrauchers wäre, dass Sie es einer Methode geben können, die einen Tomatenkonsumenten will, sowie einer Methode, die einen Apfelkonsumenten will. Hier haben wir keine.TwoTypesConsumer
bei Bedarf Zugriff auf die einschließende Instanz haben, und dann können SietwoTypesConsumer.getAppleConsumer()
an eine Methode übergeben, die einen Apple-Konsumenten haben möchte. Eine andere Möglichkeit wäre, Methoden hinzuzufügen,addConsumer(Producer<Apple> producer)
die TwoTypesConsumer ähneln.ExceptionMapper
) ...Aufgrund der Typlöschung können Sie dieselbe Schnittstelle nicht zweimal implementieren (mit unterschiedlichen Typparametern).
quelle
Hier ist eine mögliche Lösung, die auf Steve McLeods basiert :
Die implizite Anforderung der Frage war
Consumer<Tomato>
undConsumer<Apple>
Objekte, die den Status teilen. Der Bedarf anConsumer<Tomato>, Consumer<Apple>
Objekten ergibt sich aus anderen Methoden, die diese als Parameter erwarten. Ich brauche eine Klasse, die beide implementiert, um den Status zu teilen.Steves Idee war es, zwei innere Klassen zu verwenden, die jeweils einen anderen generischen Typ implementieren.
Diese Version fügt Getter für die Objekte hinzu, die die Consumer-Schnittstelle implementieren, die dann an andere Methoden übergeben werden können, die sie erwarten.
quelle
Consumer<*>
Instanzen in Instanzfeldern zu speichern, wennget*Consumer
sie häufig aufgerufen werden.Zumindest können Sie Ihre Implementierung des Versands geringfügig verbessern, indem Sie Folgendes tun:
Obst ist ein Vorfahr von Tomaten und Äpfeln.
quelle
bin nur darüber gestolpert. Es ist einfach passiert, dass ich das gleiche Problem hatte, aber ich habe es auf eine andere Weise gelöst: Ich habe gerade eine neue Schnittstelle wie diese erstellt
Leider wird dies als
Consumer<A>
und NICHT alsConsumer<B>
gegen jede Logik angesehen. Sie müssen also einen kleinen Adapter für den zweiten Verbraucher wie diesen in Ihrer Klasse erstellenWenn a
Consumer<A>
benötigt wird, können Sie einfach bestehenthis
, und wennConsumer<B>
es benötigt wird, bestehen Sie einfachconsumerAdapter
quelle
Sie können dies nicht direkt in einer Klasse tun, da die folgende Klassendefinition aufgrund des Löschens generischer Typen und der doppelten Schnittstellendeklaration nicht kompiliert werden kann.
Für jede andere Lösung zum Packen derselben Verbrauchsoperationen in eine Klasse muss Ihre Klasse wie folgt definiert werden:
Dies ist sinnlos, da Sie die Definition beider Vorgänge wiederholen / duplizieren müssen und sie nicht über die Schnittstelle referenziert werden. IMHO ist dies eine schlechte kleine und Code-Duplizierung, die ich zu vermeiden versuche.
Dies kann auch ein Indikator dafür sein, dass eine Klasse zu viel Verantwortung trägt, um zwei verschiedene Objekte zu verbrauchen (wenn sie nicht gekoppelt sind).
Was ich jedoch tue und was Sie tun können, ist, ein explizites Factory-Objekt hinzuzufügen, um verbundene Verbraucher auf folgende Weise zu erstellen:
Wenn diese Typen in Wirklichkeit wirklich gekoppelt (verwandt) sind, würde ich empfehlen, eine Implementierung folgendermaßen zu erstellen:
Der Vorteil ist, dass die Factory-Klasse beide Implementierungen kennt, es einen gemeinsamen Status gibt (falls erforderlich) und Sie bei Bedarf mehr gekoppelte Verbraucher zurückgeben können. Es gibt keine wiederholte Deklaration der Verbrauchsmethode, die nicht von der Schnittstelle abgeleitet ist.
Bitte beachten Sie, dass jeder Verbraucher eine unabhängige (noch private) Klasse sein kann, wenn er nicht vollständig verwandt ist.
Der Nachteil dieser Lösung ist eine höhere Komplexität der Klasse (auch wenn es sich um eine Java-Datei handeln kann). Um auf die Konsummethode zuzugreifen, benötigen Sie einen weiteren Aufruf, anstatt:
du hast:
Zusammenfassend kann man definieren 2 allgemeine Verbraucher in einer Top-Level - Klasse 2 innere Klassen verwenden , aber bei Aufruf müssen Sie zunächst einen Verweis erhalten auf geeignete Umsetzung der Verbraucher , da dies nicht nur ein Verbraucher Objekt sein kann.
quelle
Im funktionalen Stil ist dies recht einfach, ohne die Schnittstelle zu implementieren, und es wird auch die Überprüfung des Kompilierungszeittyps durchgeführt.
Unsere funktionale Schnittstelle zum Konsumieren von Entitäten
unser Manager, um Entität angemessen zu verarbeiten und zu konsumieren
quelle
Eine weitere Alternative, um die Verwendung weiterer Klassen zu vermeiden. (Beispiel mit Java8 +)
quelle
Entschuldigung für die Beantwortung alter Fragen, aber ich liebe es wirklich! Versuchen Sie diese Option:
Ich denke, das ist es, wonach Sie suchen.
Sie erhalten diese Ausgabe:
quelle