Die Vermeidung Der Rückkehr Wildcard-Typen
Habe ich eine Klasse mit einer Sammlung von Wildcard-Typen, die ein singleton ist, so etwas wie:
public ObliviousClass{
private static final ObliviousClass INSTANCE = new ObliviousClass();
private Map<Key, Type<?>> map = new HashMap<Key, Type<?>>();
public void putType(Key key, Type<?> type){
map.put(type);
}
//returns the singleton
public static ObliviousClass getInstance(){
return INSTANCE;
}
}
Ich würde gerne in der Lage, zum hinzufügen von verschiedenen Parametrisierten Typen, die zu dieser Kollektion, die im client-code:
void clientMethod(){
ObliviousClass oc = ObliviousClass.getInstance();
Type<Integer> intType = ...
Type<String> stringType = ...
oc.putType(new Key(0), intType);
oc.putType(new Key(1), stringType);
}
Bis zu diesem Punkt, so wie ich es verstehe, ist alles ok. Aber ein client muss in der Lage sein zu Holen ein Type<?>
sofern die Key
. So eine Methode ist etwas wie die folgende hinzu käme ObliviousClass
:
public Type<?> getType(Key key){
return map.get(key);
}
Aber in mein handy kopieren von Effektive Java, ich lese:
Verwenden Sie keine wildcard-Typen als Rückgabewerte.
Ich verstehe das Problem, wie der client müsste Besetzung der zurückgegebenen Type<?>
. Aber ich möchte wirklich nicht zu machen ObliviousClass
einen generischen Typ, ObliviousClass<T>
, weil dann mein client-code oben nicht funktionieren würde...
Ist es besser, für das, was ich versuche zu tun?
-Meine aktuelle Lösung ist es, eine statische Methode für die client; etwas entlang der Linien von:
public static <T> void getType(ObliviousClass instance, Key key, Type<T> dest){
dest = (Type<T>)instance.getType(key);
}
Suchte ich herum, aber war nicht in der Lage, eine Antwort zu finden, die ganz gelöscht meine Verwirrung.
- haben Sie als Zugabe ein generischer Typ Sie Ihre Klasse, wie so
ObliviousClass<T>
? - Tun hemmen würde mich hinzufügen, sowohl des Typs<Integer> und Geben<String> in die Kollektion. Die Klasse Type<T> hat einen generischen Typ zu erlauben, den Kunden einige notwendige Flexibilität, es zu benutzen, aber ObliviousClass ist nur dafür zuständig, ein Verweis auf jeder und nicht alles tun, mit Ihnen, direkt. So pflegen Sie als wildcard-Typen geeignet scheint, für mich, aber abrufen, ist meine Frage.
- Spät zur party, aber...
Du musst angemeldet sein, um einen Kommentar abzugeben.
Hier ist eine Typ-sichere Weise zu speichern, mehrere Instanzen eines bestimmten Typs in einer Karte. Der Schlüssel ist, dass Sie brauchen, um eine
Class
Beispiel beim abrufen von Werten zur Durchführung der Laufzeit-Typ-Prüfung, weil der statische Typ-information gelöscht wurde.Nutzung würde wie folgt Aussehen:
oc.get(k1, Type<Integer>.class);
. Gilt das auch funktionieren?Class
Instanz, die Sie brauchen würde. Wenn Sie verlängernType<T>
, die den Typ-parameter in Ihrer Erweiterung (z.B.class IntegerType extends Type<Integer> {...}
), dann wird der type-parameter (Integer
, in diesem Beispiel, ist nicht gelöscht, und Sie können es per reflection ausführen der Form. Dies wird als ein "super-Typ-token." Es gibt jedoch einige Probleme mit diesem Ansatz; siehe Neal Gafter ' s blog.Geben Sie einfach Ihre Klasse:
Zur info, an dieser Stelle haben Sie die delegation Muster im Spiel.
Ihrem Beispiel-client-code würde erklären müssen zwei Instanzen
ObliviousClass
:ObliviousClass<String>
undObliviousClass<Integer>
.Edit:
Wenn Sie muss haben ein gemischter Haufen von Typen, die Sie auferlegen kann, eine geben Sie auf Ihrer Methode, aber Sie erhalten eine compiler-Warnung für eine unsichere cast:
Kunden können die Aufrufe dieser Methoden wie diese:
Treffen Sie Ihre Wahl, was Sie bevorzugen und verwenden.
ObliviousClass
, das kann möglicherweise sogar ein singleton zu halten, eine Sammlung vonType<?>
und nicht um das, was die eigentliche parametrisierte Typen gehalten werden.Für diejenigen, die Landung auf diese Frage viele Jahre später, das ist nicht wie Java-generics sind entworfen, um verwendet werden. (Ich wollte kommentieren, aber hatte mehr details.)
Den generischen Muster schafft eine single übergeordneten Klasse pro Typ, ID, anstatt mehrere verschiedene Klassen. Betrachten wir die einfachere List<T>, eine Liste von Zeichenfolgen ODER ganzen zahlen (als Liste<String> oder List<Integer>) ist wie generics definiert sind. Eine Klasse pro Typ. Auf diese Weise gibt es ein einheitliches geben, wenn die Werte referenziert werden. Die Speicherung von nicht verwandten Arten wäre das gleiche wie List<Object>. Nur der Programmierer kann wissen, wenn Sie mehrere Arten gespeichert werden und wie, um diese mit dem Gießen.
Wäre es ok zum speichern von Unterklassen zu einer übergeordneten Klasse, aber wenn der Zugriff aus der Sammlung, ohne casting, die Eltern der Klasse Kontakt, das ist alles bekannt. Zum Beispiel, eine generische collection-definiert und mit einer Schnittstelle wie Map<String, Runnable>. Jedoch nur die Methode run() ist auch dann sichtbar, wenn andere öffentliche Methoden Hinzugefügt werden Implementierungen (es sei denn, der Programmierer explizit wirft). Zusätzliche Methoden, casting notwendig ist.
Dies ist eine Einschränkung in Java. Eine Sprache definiert werden könnte, um zu wissen, der L-Wert geben - auch Java. Aber es war nicht. Wenn neue Funktionen Hinzugefügt werden, es gibt viele abwärtskompatibel überlegungen [Sun und Oracle berücksichtigt. Code kompiliert mit Generika wurde entwickelt, um laufen auf älteren JVMs mit type erasure. Benutzt Java type erasure bei der Kompilierung sobald es festgestellt hat, dass die Generika sind konsequent Referenz. Der bytecode verwendet Objekt, als wenn die Instanz war (sozusagen), definiert als Liste. Wenn die Wahl getroffen wurde, zu verlassen Abwärtskompatibilität, wie Java 9 und 11, dann mehrere Arten könnte wurden praktikabel.