2013-02-13 5 views
5

Ich habe Code in meinem Projekt, das wie folgt aussieht:Ist das in meiner generischen Methode sicher?

public interface Bar<T extends Foo<?>> { 
//... 
} 

public class MyFoo implements Foo<String> { 
    private List<Bar<Foo<String>> barFoo = ... 

    public <U extends Foo<String>> boolean addBar(Bar<? extends U> b) { 
     barFoo.add((Bar<Foo<String>>) b); //safe cast? 
    } 

} 

Eclipse-gibt eine Warnung für die Besetzung in addBar, die die Besetzung unsicher ist. Allerdings bin ich richtig in der Annahme, dass die Besetzung nicht werfen wird angesichts der Beschränkungen, die ich auf die Typparameter gesetzt habe, und deshalb ist die Besetzung in der Tat sicher?

Antwort

6

Nicht im Allgemeinen.

Angenommen Bar hat eine Methode void get(T value), und es gibt zwei Implementierungen von Foo<String>, MyFoo und YourFoo. Angenommen, ein Anrufer ruft addBar auf einem Wert vom Typ Bar<MyFoo>. Das funktioniert: Wenn U = Foo<String>, haben wir Bar<MyFoo> ist ein Untertyp von Bar<? extends U>. Jetzt werfen wir diesen Wert auf Bar<Foo<String>>. Wenn Bar keine Methoden hat, die T als Argumente akzeptieren, gibt es kein Problem. Aber angenommen, es hat eine Methode void process(T value). Die Implementierung, die wir aufgerufen haben, hat T = MyFoo, so dass es nur eine process(MyFoo value) Methode hat. Sobald wir es jedoch in eine Bar<Foo<String>> umwandeln, können wir es stattdessen mit einer YourFoo aufrufen. Das ist illegal.

Stab im Dunkeln, aber ich vermute, dass was Sie wirklich wollten, war barFoo als List<? extends Bar<? extends Foo<String>> zu deklarieren.

+0

Ausgezeichnete Antwort. Vielen Dank! –

+0

@DongieAgnir Sie sind wahrscheinlich in Ordnung mit 'List > 'und' public boolean addBar (Balken >) ' – irreputable

2

Dies ist keine sichere Besetzung. Eclipse ist korrekt.

Stellen Sie eine Klasse hat MyFoo die Foo und Sie bestanden in einer Bar<MyFoo<String>> nun einige Verfahren in Bar mit einer myMethod(Foo x) Unterschrift erstreckt, wenn nur eine myMethod(MyFoo x) Signatur erstellt wurde, so würde die Methode Lookup fehlschlagen.

+0

Sie können keine Klasse, die String erweitert, da es endgültig ist, können Sie? –

+1

@CyrilleKarmann: Mel Nicholson schrieb _Imagine ..._ – jlordo

+0

Dies ist ein guter Punkt, und ist ein Teil davon, wie unveränderlich für String ist. Die gleiche Logik gilt, wenn Sie MyFoo verwenden, um Foo zu erweitern, und wir nehmen an, dass Foo nicht endgültig ist. –

0

Die Besetzung ist nicht sicher, weil, obwohl UFoo<String> erstreckt, ist es nicht (notwendigerweise) der Fall ist, dass Bar<U>Bar<Foo<String>> erstreckt. Tatsächlich wird sich Bar<U> nur Bar<Foo<String>> erstrecken, wenn sie das gleiche sind, d. H. Wenn UFoo<String> ist.

Intuitiv kann es scheinen, dass (zum Beispiel) List<String> ein Untertyp von List<Object> sein sollte, aber das ist nicht, wie Generika funktionieren. List<String> ist ein Untertyp von List<? extends Object>, aber es ist nicht ein Untertyp von List<Object>. (Es kann mehr Sinn machen, ein Beispiel wie Comparable<T> zu berücksichtigen: Comparable<String> bedeutet „verglichen werden können, um jede String, während Comparable<Object> bedeutet‚verglichen werden können, um jede Object‘Es sollte klar sein, dass Comparable<String> kein Subtyp von Comparable<Object> sein sollte..)

[& hellip;] die Besetzung werfen wird nicht [& hellip;] und damit die Besetzung in der Tat sicher ist

ich glaube, du Missverständnis die Art der Warnung?.Eclipse warnt dich, dass dieser Cast nicht werfen wird, auch wenn es, und das ist eigentlich warum ist es nicht sicher ist. Zum Beispiel ist dieser Code:

final Object o = Integer.valueOf(7); 
final String s = (String) o; 

ist absolut sicher, weil die Besetzung eine Ausnahme werfen wird. Aber dieser Code:

final List<?> wildcardList = new ArrayList<Integer>(Integer.valueOf(7)); 
final List<String> stringList = (List<String>) wildcardList; 

ist unsicher, weil die Laufzeit keine Möglichkeit zur Überprüfung der Besetzung hat (aufgrund der Löschung), so dass es nicht eine Ausnahme auslösen, auch wenn es falsch ist: stringList ist jetzt ein List<String>, dessen erstes Element vom Typ Integer ist. (Was passiert, ist, irgendwann später, können Sie eine spontane ClassCastException bekommen, wenn Sie versuchen, etwas mit diesem Element zu tun.)

Verwandte Themen