Java Generics Wildcarding mit mehreren Klassen

385

Ich möchte ein Class-Objekt haben, aber ich möchte jede Klasse, die es darstellt, zwingen, Klasse A zu erweitern und Schnittstelle B zu implementieren.

Ich kann:

Class<? extends ClassA>

Oder:

Class<? extends InterfaceB>

aber ich kann nicht beides. Gibt es eine Möglichkeit, dies zu tun?

Alex Beardsley
quelle

Antworten:

613

Eigentlich Sie können tun , was Sie wollen. Wenn Sie mehrere Schnittstellen oder eine Klasse plus Schnittstellen bereitstellen möchten, muss Ihr Platzhalter ungefähr so ​​aussehen:

<T extends ClassA & InterfaceB>

Weitere Informationen finden Sie im Generika-Tutorial unter sun.com, insbesondere im Abschnitt Parameter für gebundene Typen, unten auf der Seite. Sie können tatsächlich mehr als eine Schnittstelle auflisten, wenn Sie dies wünschen, und zwar & InterfaceNamefür jede, die Sie benötigen.

Dies kann beliebig kompliziert werden. Weitere Informationen finden Sie in der JavaDoc-Deklaration von Collections#max(in zwei Zeilen eingeschlossen):

public static <T extends Object & Comparable<? super T>> T
                                           max(Collection<? extends T> coll)

warum so kompliziert? Wie in den häufig gestellten Fragen zu Java Generics erwähnt: Um die Binärkompatibilität zu gewährleisten .

Es sieht so aus, als würde dies bei der Variablendeklaration nicht funktionieren, aber es funktioniert, wenn einer Klasse eine generische Grenze gesetzt wird. Um zu tun, was Sie wollen, müssen Sie möglicherweise durch ein paar Reifen springen. Aber du kannst es schaffen. Sie können so etwas tun, indem Sie Ihrer Klasse eine generische Grenze setzen und dann:

class classB { }
interface interfaceC { }

public class MyClass<T extends classB & interfaceC> {
    Class<T> variable;
}

um variabledas zu bekommen hat die Einschränkung, die Sie wollen. Weitere Informationen und Beispiele finden Sie auf Seite 3 von Generics in Java 5.0 . Beachten Sie, dass in <T extends B & C>der Klasse der Name an erster Stelle stehen muss und die Schnittstellen folgen. Und natürlich können Sie nur eine Klasse auflisten.

Eddie
quelle
94
Das ist hilfreich. Es ist erwähnenswert , dass die Klasse muss zuerst kommt, kann man nicht sagen ‚<T erweitert InterfaceB & KlasseA>‘.
EricS
5
Wie machen Sie dasselbe für T, wenn Sie entweder eine Klasse erweitern ODER eine Schnittstelle implementieren?
Ragunath Jawahar
1
@RagunathJawahar: Du kannst nicht zu offen darüber werden. Sie müssen einige Grenzen haben, und eine davon besteht darin, im Voraus zu wissen, wo Sie Schnittstellen haben, wo Sie Klassen haben und wie Sie die Vererbung verwenden. Sie müssen für einen Typparameter ziemlich genau wissen, ob es sich um eine Klasse oder eine Schnittstelle handelt.
Eddie
4
In der ersten Zeile Ihrer Antwort heißt es: "Lassen Sie Ihre Wildcard ungefähr so ​​aussehen." Es gibt kein ?in diesem Ausdruck, also ist es wirklich eine "Wildcard"? Ich frage, weil ich nicht das gesamte Konzept "Zwei Dinge gleichzeitig erweitern" zum Laufen bringen kann, wenn der Typparameter wirklich ein Platzhalter ist.
The111
1
Ja, es ist nicht wirklich ein Platzhalter und Sie können nicht das tun, was das OP mit einem Platzhalter verlangt hat. Sie können effektiv das tun, was das OP wollte, nur nicht mit einem Platzhalter.
Eddie
17

Sie können dies nicht mit "anonymen" Typparametern (dh Platzhaltern, die verwendet werden ?) tun, aber Sie können es mit "benannten" Typparametern tun. Deklarieren Sie einfach den Typparameter auf Methoden- oder Klassenebene.

import java.util.List;
interface A{}
interface B{}
public class Test<E extends B & A, T extends List<E>> {
    T t;
}
user2861738
quelle
2
Warum sind Platzhalter nicht erlaubt? dh ich verstehe nicht, warum dies nicht gültig sein sollte: ArrayList <? erweitert ClassA & InterfaceB> Liste; Und dann , wenn Sie ein Element aus der Liste gezogen wird , könnte man es auf eine Variable vom Typ KlasseA oder vom Typ InterfaceB zuweisen
Mark
Das Ersetzen des Platzhalters durch eine Variable ist eine großartige Technik! Aber es funktioniert nicht immer. Beispielsweise erlaubt Java keine Variablen vom Typ Namen in Anmerkungswerttypen, während Platzhalter zulässig sind.
Sigpwned