Stefan Matthias Aust
2004-06-29 08:24:43 UTC
Ich versuche gerade mit dem neusten Cheetah (was offenbar keine Probleme
mehr mit foreach hat) mein Webconflict-Beispielprogramm zu generifizieren.
Hier ein Beispiel. Eigentlich nichts besonderes, ich filtere halt eine
Liste aller Raumschiffe nach den Typen (die ich leichtfertigerweise als
Unterklassen realisiert habe - würde ich jetzt nicht mehr machen, da es
vieles umständlicher macht - wir werden das gleich sehen...)
class Planet {
private final List ships = new ArrayList();
List getBattleShips() {
return getShipsOfClassAndOwner(BattleShip.class, null);
}
List getColonyShips() {
return getShipsOfClassAndOwner(ColonyShip.class, null);
}
private List getShipsOfClassAndOwner(Class c, Player p) {
List result = new ArrayList();
for (Iterator i = ships.iterator(); i.hasNext();) {
Ship s = (Ship) i.next();
if (c.isInstance(s) && (p == null || p == c.getOwner())
result.add(s);
}
return result;
}
}
Die scheinbar offensichtliche Änderung ist
private final List<Ship> ships = new ArrayList<Ship>();
List<BattleShip> getBattleShips() {
return getShipsOfClassAndOwner(BattleShip.class, null);
}
Doch wie muss man meine private Methode typen? Der Rückgabetyp darf
micht einfach List<Ship> sein, denn das ist nicht kompatibel zu
List<BattleShip>, auch wenn BattleShip eine Unterklasse von Ship ist.
Glücklicherweise sind Klassen als Class<T> getypt und T ist jeweils der
Typ der Klasse. Das kann man jetzt zusammen mit einer generischen
Methode ausnutzen:
private <S extends Ship> List<S> getShipsOfClassAndOwner(Class<S> c ...
Das erste <S extends Ship> definiert eine Typvariable S, die eine
Unterklasse von Ship (oder Ship selbst) sein muss. Dann gibt jetzt der
Klassenparameter an der Aufrufstelle den Wert für S vor und alles wird
gut..? Noch nicht ganz. Dies ist der neue Code:
List<S> result = new ArrayList<S>();
for (S ship : ships) {
if (c.isInstance(s) && (p == null || p == c.getOwner())
result.add(s);
}
return result;
Das for funktioniert so nicht, ships ist nicht Zuweisungskompatibel,
denn S ist kein Ship. Ich muss also die Variable ships anders typen.
Das sind eben nicht nur Ship-Exemplare (tatsächlich sind das niemals
solche Exemplare, denn das ist eine abstrakte Klasse) sondern Exemplare
der Unterklassen von Ship.
private final List<? extends Ship> ships = new ArrayList<Ship>();
Nun lässt sich die Beispielmethode übersetzen. Aber dafür habe ich mir
ein halbes Dutzend andere Fehler eingehandelt... Ich kann sie alle durch
stärke Spezialisierung oder Einsatz von wildcards korrigieren.
Aber diese unschuldig aussehende Methode funktioniert nicht mehr:
void addShip(Ship ship) {
ships.add(ship);
}
Und ich verstehe nicht, warum nicht...
"List<E>" definiert "boolean add(E e)". Warum sagt Eclipse "Unsafe
wildcard operation: The method add(? extends Ship) of type List<?
extends Ship> is not applicable for the arguments (Ship)" und javac
sogar "cannot find symbol".
bye
mehr mit foreach hat) mein Webconflict-Beispielprogramm zu generifizieren.
Hier ein Beispiel. Eigentlich nichts besonderes, ich filtere halt eine
Liste aller Raumschiffe nach den Typen (die ich leichtfertigerweise als
Unterklassen realisiert habe - würde ich jetzt nicht mehr machen, da es
vieles umständlicher macht - wir werden das gleich sehen...)
class Planet {
private final List ships = new ArrayList();
List getBattleShips() {
return getShipsOfClassAndOwner(BattleShip.class, null);
}
List getColonyShips() {
return getShipsOfClassAndOwner(ColonyShip.class, null);
}
private List getShipsOfClassAndOwner(Class c, Player p) {
List result = new ArrayList();
for (Iterator i = ships.iterator(); i.hasNext();) {
Ship s = (Ship) i.next();
if (c.isInstance(s) && (p == null || p == c.getOwner())
result.add(s);
}
return result;
}
}
Die scheinbar offensichtliche Änderung ist
private final List<Ship> ships = new ArrayList<Ship>();
List<BattleShip> getBattleShips() {
return getShipsOfClassAndOwner(BattleShip.class, null);
}
Doch wie muss man meine private Methode typen? Der Rückgabetyp darf
micht einfach List<Ship> sein, denn das ist nicht kompatibel zu
List<BattleShip>, auch wenn BattleShip eine Unterklasse von Ship ist.
Glücklicherweise sind Klassen als Class<T> getypt und T ist jeweils der
Typ der Klasse. Das kann man jetzt zusammen mit einer generischen
Methode ausnutzen:
private <S extends Ship> List<S> getShipsOfClassAndOwner(Class<S> c ...
Das erste <S extends Ship> definiert eine Typvariable S, die eine
Unterklasse von Ship (oder Ship selbst) sein muss. Dann gibt jetzt der
Klassenparameter an der Aufrufstelle den Wert für S vor und alles wird
gut..? Noch nicht ganz. Dies ist der neue Code:
List<S> result = new ArrayList<S>();
for (S ship : ships) {
if (c.isInstance(s) && (p == null || p == c.getOwner())
result.add(s);
}
return result;
Das for funktioniert so nicht, ships ist nicht Zuweisungskompatibel,
denn S ist kein Ship. Ich muss also die Variable ships anders typen.
Das sind eben nicht nur Ship-Exemplare (tatsächlich sind das niemals
solche Exemplare, denn das ist eine abstrakte Klasse) sondern Exemplare
der Unterklassen von Ship.
private final List<? extends Ship> ships = new ArrayList<Ship>();
Nun lässt sich die Beispielmethode übersetzen. Aber dafür habe ich mir
ein halbes Dutzend andere Fehler eingehandelt... Ich kann sie alle durch
stärke Spezialisierung oder Einsatz von wildcards korrigieren.
Aber diese unschuldig aussehende Methode funktioniert nicht mehr:
void addShip(Ship ship) {
ships.add(ship);
}
Und ich verstehe nicht, warum nicht...
"List<E>" definiert "boolean add(E e)". Warum sagt Eclipse "Unsafe
wildcard operation: The method add(? extends Ship) of type List<?
extends Ship> is not applicable for the arguments (Ship)" und javac
sogar "cannot find symbol".
bye
--
Stefan Matthias Aust // "Zweifel sind der Ansporn des Denkens..." -U
Stefan Matthias Aust // "Zweifel sind der Ansporn des Denkens..." -U