Discussion:
Generics - Problem mit add()
(zu alt für eine Antwort)
Stefan Matthias Aust
2004-06-29 08:24:43 UTC
Permalink
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
--
Stefan Matthias Aust // "Zweifel sind der Ansporn des Denkens..." -U
Christoph Jerolimov
2004-06-29 09:27:03 UTC
Permalink
Post by Stefan Matthias Aust
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
private <S extends Ship> List<S> getShipsOfClassAndOwner(Class<S> c ...
Kleine Frage, spiel auch gerade was, aber wie gebe ich den Typ für '<S
extends Ship>' beim Aufruf an?
Post by Stefan Matthias Aust
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
List<S> result = new ArrayList<S>();
for (S ship : ships) {
if (c.isInstance(s) && (p == null || p == c.getOwner())
result.add(s);
}
return result;
Nur so eine Idee, statt 'S ship' 'Ship ship'

List<S> result = new ArrayList<S>();
for (Ship ship : ships) {
if (c.isInstance(s) && (p == null || p == c.getOwner())
result.add(s);
}
return result;
Post by Stefan Matthias Aust
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.
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
gruß Christoph
Stefan Matthias Aust
2004-06-29 09:39:58 UTC
Permalink
Post by Christoph Jerolimov
Post by Stefan Matthias Aust
private <S extends Ship> List<S> getShipsOfClassAndOwner(Class<S> c ...
Kleine Frage, spiel auch gerade was, aber wie gebe ich den Typ für '<S
extends Ship>' beim Aufruf an?
Den inferiert (analysiert, ermittelt) Java beim Aufruf automatisch aus
der Klassen-Konstante. Die Klasse Class hat ebenfalls eine
Klassenvariable. Die Konstante "BattleShip.class" hat den Typ
Class<BattleShip>. Daraus kann Java jetzt über Class<S> auf
S=BattleShip rückschließen.
Post by Christoph Jerolimov
Post by Stefan Matthias Aust
List<S> result = new ArrayList<S>();
for (S ship : ships) {
if (c.isInstance(s) && (p == null || p == c.getOwner())
result.add(s);
}
return result;
Nur so eine Idee, statt 'S ship' 'Ship ship'
Nein, das wäre ein Fehler. Ich muss gerade S statt Ship benutzen, weil
das ja eben ein BattleShip oder ColonyShip sein kann.


bye
--
Stefan Matthias Aust // "Zweifel sind der Ansporn des Denkens..." -U
Christoph Jerolimov
2004-06-29 09:52:37 UTC
Permalink
Post by Stefan Matthias Aust
Post by Christoph Jerolimov
Post by Stefan Matthias Aust
List<S> result = new ArrayList<S>();
for (S ship : ships) {
if (c.isInstance(s) && (p == null || p == c.getOwner())
result.add(s);
}
return result;
Nur so eine Idee, statt 'S ship' 'Ship ship'
Nein, das wäre ein Fehler. Ich muss gerade S statt Ship benutzen, weil
das ja eben ein BattleShip oder ColonyShip sein kann.
Du gehst allerdings eine Liste mit Ship's durch, zu den "Zeitpunkt" der
for-Schleife sind ja noch beide Typen möglich, daher muss es meiner
Meinung nach Ship sein. Dann kannst du die Überprüfung auf isInstance
machen (würde nicht auch S.class.isInstance funktionieren?), dann ein
explizieter Cast nach S und ab in die Ergebnis-Liste.

Gruß Christoph
Stefan Matthias Aust
2004-06-29 10:11:29 UTC
Permalink
Post by Christoph Jerolimov
(würde nicht auch S.class.isInstance funktionieren?)
Nein, denn die Typvariable S existiert zur Laufzeit nicht und daher kann
man davon auch kein "class" Attribut abfragen.


bye
--
Stefan Matthias Aust // "Zweifel sind der Ansporn des Denkens..." -U
Christoph Jerolimov
2004-06-29 10:24:12 UTC
Permalink
Post by Stefan Matthias Aust
Post by Christoph Jerolimov
(würde nicht auch S.class.isInstance funktionieren?)
Nein, denn die Typvariable S existiert zur Laufzeit nicht und daher kann
man davon auch kein "class" Attribut abfragen.
Jein, S existiert nicht, da hast du recht, S.class definiert er trotzdem
als 'class Number'.
Post by Stefan Matthias Aust
bye
Christoph Jerolimov
2004-06-29 10:05:50 UTC
Permalink
Post by Stefan Matthias Aust
Post by Christoph Jerolimov
Post by Stefan Matthias Aust
private <S extends Ship> List<S> getShipsOfClassAndOwner(Class<S> c ...
Kleine Frage, spiel auch gerade was, aber wie gebe ich den Typ für '<S
extends Ship>' beim Aufruf an?
Den inferiert (analysiert, ermittelt) Java beim Aufruf automatisch aus
der Klassen-Konstante. Die Klasse Class hat ebenfalls eine
Klassenvariable. Die Konstante "BattleShip.class" hat den Typ
Class<BattleShip>. Daraus kann Java jetzt über Class<S> auf
S=BattleShip rückschließen.
Aso, das muste ich gerade ein paar mal lesen um's zu verstehen ;-)

Aber dann funktioniert's ja sogar...
Post by Stefan Matthias Aust
import java.util.ArrayList;
import java.util.List;
public class JavaEinsFuenf<T> {
static public final List<Number> zahlen = new ArrayList<Number>();
static {
zahlen.add(new Integer(8361));
zahlen.add(new Float(932));
zahlen.add(new Integer(1111));
zahlen.add(new Float(356));
}
static public void main(String[] args) {
List<Integer> list = getEntriesByClass(Integer.class);
for (Integer i : list) {
System.out.println(i);
}
}
static public <S extends Number> List<S> getEntriesByClass(Class<S> c) {
List<S> result = new ArrayList<S>();
for (Number n : zahlen) {
if (c.isInstance(n))
result.add((S)n);
}
return result;
}
}
endlich ohne warning, hope so.

gruß Christoph
Stefan Matthias Aust
2004-06-29 10:10:45 UTC
Permalink
Post by Christoph Jerolimov
Aber dann funktioniert's ja sogar...
Schön :) Die for-Schleife war aber eigentlich nicht mein Problem,
sondern das add() auf die List<? extends Ship>.


bye
--
Stefan Matthias Aust // "Zweifel sind der Ansporn des Denkens..." -U
Christoph Jerolimov
2004-06-29 10:30:23 UTC
Permalink
Post by Stefan Matthias Aust
void addShip(Ship ship) {
ships.add(ship);
}
[...]
Post by Stefan Matthias Aust
Schön :) Die for-Schleife war aber eigentlich nicht mein Problem,
sondern das add() auf die List<? extends Ship>.
Versteh ich nicht, in meinem Zahlenbeispiel funktioniert das auch:

static void add(Number n) {
zahlen.add(n);
}




btw: Eclipse RC2 meckert recht oft über meinen Code, u.a. bei recht
simplen Aktionen wie Ausschneiden etc. internal NullPointerExcept...
Stefan Matthias Aust
2004-06-29 10:35:44 UTC
Permalink
Grummel. Ich habe jetzt aber keine Lust zu suchen, ob es ein Java oder
Eclipse-Problem ist.
Post by Christoph Jerolimov
btw: Eclipse RC2 meckert recht oft über meinen Code, u.a. bei recht
simplen Aktionen wie Ausschneiden etc. internal NullPointerExcept...
Dann nimm die final 3.0, kein Grund mehr für eine Vorversion.


bye
--
Stefan Matthias Aust // "Zweifel sind der Ansporn des Denkens..." -U
Stefan Matthias Aust
2004-06-30 07:04:37 UTC
Permalink
Komisch. Hier ist mein zusammengestrichener Fall:

abstract class A {}
class B extends A {}
class C extends A {}
public class T {
final List<? extends A> list = new ArrayList<A>();
void add(A a) {
list.add(a);
}
}

Und der liefert bei Eclipse den Fehler "Unsafe wildcard operation: The
method add(? extends A) of type List<? extends A> is not applicable for
the arguments (A)" und bei javac den Fehler

cannot find symbol
symbol : method add(A)
location: interface java.util.List<capture of ? extends A>

Und ich verstehe das immer noch nicht, weil ich dachte, dass "<? extends
A>" der korrekte Typ für eine Liste von Unterklassen von A wäre.

Wie auch immer, ich bin jetzt zurück auf List<Ship> und muss jetzt in
meiner Methode getShipsOfClassAndOwner einen Cast auf S benutzen. :(


bye
--
Stefan Matthias Aust // "Zweifel sind der Ansporn des Denkens..." -U
Stefan Matthias Aust
2004-06-30 07:10:53 UTC
Permalink
Post by Stefan Matthias Aust
Wie auch immer, ich bin jetzt zurück auf List<Ship> und muss jetzt in
meiner Methode getShipsOfClassAndOwner einen Cast auf S benutzen. :(
Mist. Das schluckt zwar Eclipse, aber javac meldet eine "unchecked
cast" Warnung...

Kann ich die Methode, aus einer Liste von Unterklassen von A eine Liste
von bestimmten Exemplaren zu ermitteln, irgendwie anders typen? Sowas
brauche ich irgendwie häufig...

<X extends A> List<X> get(Class<X> c) {
List<X> l = new ArrayList<X>();
for (A a : list) {
if (c.isInstance(a)) l.add((X) a);
}
return l;
}


bye
--
Stefan Matthias Aust // "Zweifel sind der Ansporn des Denkens..." -U
Bernd Eckenfels
2004-06-30 07:48:05 UTC
Permalink
Post by Stefan Matthias Aust
Mist. Das schluckt zwar Eclipse, aber javac meldet eine "unchecked
cast" Warnung...
Kann ich die Methode, aus einer Liste von Unterklassen von A eine Liste
von bestimmten Exemplaren zu ermitteln, irgendwie anders typen? Sowas
brauche ich irgendwie häufig...
Du könntest ei gemeinsames Interface nehmen(?) und keine extends Beziehung
fordern? Aber dein Beispiel sollte man auf jedenfall mal in Sun foren
diskutieren.

Gruss
Bernd
--
eckes privat - http://www.eckes.org/
Project Freefire - http://www.freefire.org/
Wolfgang Eibner
2004-06-30 10:02:29 UTC
Permalink
Post by Stefan Matthias Aust
abstract class A {}
class B extends A {}
class C extends A {}
public class T {
final List<? extends A> list = new ArrayList<A>();
void add(A a) {
list.add(a);
}
}
...
Und ich verstehe das immer noch nicht, weil ich dachte, dass "<? extends
A>" der korrekte Typ für eine Liste von Unterklassen von A wäre.
Jep, es ist eine Liste von Unterklassen von A. Aber der Compiler kann nicht
sicherstellen welche Liste sich darin befindet, da er ja das new
ArrayList<A>() nicht beachtet, sondern nur die Referenz. Und einer Liste vom
Typ List<B> (welche ja eine List<? extends A> wäre) kannst du keine A's
adden.
Außerdem sind generische Typen invariant und eine Zuweisung List<A> =
List<B> ist nicht möglich.
Post by Stefan Matthias Aust
Wie auch immer, ich bin jetzt zurück auf List<Ship> und muss jetzt in
meiner Methode getShipsOfClassAndOwner einen Cast auf S benutzen. :(
Da wirst du wohl net drumrum kommen. Schau dir mal
javax.swing.event.EventListenerList an. Dort ist es genauso implimentiert
(im Prinzip). Gibt halt nur leider ne Warning wegen eines unchecked Casts...
Manchmal würd ich mir ein Konstrukt wünschen Java mitzuteilen, dass der Cast
garantiert sicher ist, da vorher bereits ein Test durchgeführt wurde, ob das
Objekt auch den Typ besitzt...

Eine andere Möglichkeit für dich wären zwei getrennte Listen, aber das is
Designmäßig auch net schön...

mfg.
Wolfgang
Stefan Matthias Aust
2004-06-30 10:34:54 UTC
Permalink
Post by Wolfgang Eibner
Jep, es ist eine Liste von Unterklassen von A. Aber der Compiler kann nicht
sicherstellen welche Liste sich darin befindet
Ah, jetzt kapiere ich das. Ich habe ja eine Variable definiert, der mal
verschiedene List<?> zuweisen kann, mit ? eben Unterklasse von A. Was
ich wollte aber, eine Liste zu definieren, die A und Unterklassen von A
aufnehmen kann und das ist mit List<A> genau richtig. Bin darauf
reingefallen, dass ich ja den Typ der Variablen und nicht den der Liste
definiere. Danke.
Post by Wolfgang Eibner
Da wirst du wohl net drumrum kommen. Schau dir mal
javax.swing.event.EventListenerList an. Dort ist es genauso implimentiert
Hi, hi, die haben genau das gleiche Problem...
Post by Wolfgang Eibner
Manchmal würd ich mir ein Konstrukt wünschen Java mitzuteilen, dass der Cast
garantiert sicher ist, da vorher bereits ein Test durchgeführt wurde, ob das
Objekt auch den Typ besitzt...
Das müsste man gar nicht mitteilen, dass könnte der Compiler selbst
wissen. Ein

if (x instanceof Y) { ... }

sollte reichen, um sicherzustellen, dass x in dem Block den Typ Y hat.

Die Sprache Nice macht das so und ich begreife nicht, warum Java das
nicht auch so macht. Würde das lästige Konstrukt

if (x instanceof Y) {
Y y = (Y) x;
...
}

einsparen und bei 1.5 auch die doofe Warnung vermeiden...
Post by Wolfgang Eibner
Eine andere Möglichkeit für dich wären zwei getrennte Listen, aber das is
Designmäßig auch net schön...
Nein ist es nicht. Bei Meinen Schiffe, von denen ich nur zwei
Unterklasen habe wäre das noch okay. Aber ich habe das Problem noch ein
zweites Mal in größer, wo ich eine Order-Klasse mit 11 Unterklassen
habe, jeweils pro Befehl eine. In einer guten Programmiersprache hätte
ich vielleicht sowas wie

orders all: BuildOrder do: [... ]

benutzt und hatte auch in Java einen passenden Filter implementiert.
Doch das ist dort blöd und ich bin wieder bei einer einfachen
for-Schleife gelandet

for (Order o: orders)
if (o instanceof BuildOrder) o.execute();

Ich brauche die Typen eigentlich nur, um die Reihenfolge der Befehle
festzulegen und glaube daher, dass ich da irgendwie noch die falsche
Modellierung habe. Doch jeder Befehl hat ja verschiedene Parameter für
ich die unterschiedliche Instanzvariablen und Konstruktoren brauche.

Ich war schon so weit und dachte über einen Metabefehl nach, so in diese Art

class Order {
Map<Object, Object> props = new HashMap();
static Order make(Object... props) {
Order o = new Order();
for (int i = 0; i < props.length; i += 2) {
o.props.put(props[i], props[i + 1]);
}
}
void execute() {
switch (kind()) {
case 0: ....
case 1: ....
}
}
private int kind() {
return kinds.get(props.get("kind")).intValue();
}
static Map<Object, Integer> kinds = new HashMap();
static {
kinds.put("build", 1);
kinds.put("combat", 2);
}
}

Aber das ist so furchtbar umständlich in Java.


bye
--
Stefan Matthias Aust // "Zweifel sind der Ansporn des Denkens..." -U
Wolfgang Eibner
2004-06-30 11:16:01 UTC
Permalink
<snip> Liste </snip>
Post by Wolfgang Eibner
Manchmal würd ich mir ein Konstrukt wünschen Java mitzuteilen, dass der Cast
garantiert sicher ist, da vorher bereits ein Test durchgeführt wurde, ob das
Objekt auch den Typ besitzt...
Das müsste man gar nicht mitteilen, dass könnte der Compiler selbst
wissen. Ein
if (x instanceof Y) { ... }
sollte reichen, um sicherzustellen, dass x in dem Block den Typ Y hat.
Jep, aber notfalls würd ich auch ein eigenes Schlüsselwort oder so nehmen.
Sollte/Könnte man sowas net mal bei Sun requesten?
<snip> Order-Problem </snip>
Ich brauche die Typen eigentlich nur, um die Reihenfolge der Befehle
festzulegen und glaube daher, dass ich da irgendwie noch die falsche
Modellierung habe. Doch jeder Befehl hat ja verschiedene Parameter für
ich die unterschiedliche Instanzvariablen und Konstruktoren brauche.
Hm, und warum nicht eine sortierte Collection nehmen und die Orders
Comparable implementieren lassen. So hättest du dann beim Ausführen schon
mal sicher die richtige Reihenfolge. Du könntest trotzdem ne BuildOrder und
ne CombatOrder mit jeweils ner execute Methode haben (die abstrakt in Order
definiert ist) und die Orders durchlaufen und execute() aufrufen. Sollte es
nötig sein, kann die execute Methode ja ne variable Parameterliste auch noch
kriegen.
<snip> Metabefehl </snip>
Wenn schon würd ich die Typen bzw. kind als enum definieren und make als
ersten Parameter übergeben.

class Order {
public enum Type {BUILD, COMBAT}

Map<String, Object> props = new HashMap<String, Object>();
Type t;

static Order make(Type t, Object... props) {
Order o = new Order();
o.t=t;
for (int i = 0; i < props.length; i += 2) {
o.props.put((String) props[i], props[i + 1]);
}

void execute() {
switch(t) {
case BUILD:...
case COMBAT:...
}
}
...
}

Da fällt mir ein, dass man die Enums ev. auch zum Comparable Vergleich für
die erste Lösung nehmen könnte... bzw. Warum nicht nur eine Order (wie beim
zweiten Vorschlag) mit ner variablen execute-Parameterliste?

mfg.
Wolfgang

PS: Ich freue mich dem großen SMA bei seinem List-Problem geholfen zu haben
;-)
PPS: An welchem Weltraum-Shooter bastelst du eigentlich?
Stefan Matthias Aust
2004-07-01 09:35:48 UTC
Permalink
Post by Wolfgang Eibner
Hm, und warum nicht eine sortierte Collection nehmen und die Orders
Comparable implementieren lassen. So hättest du dann beim Ausführen schon
mal sicher die richtige Reihenfolge.
Weil ich alle Befehle einer Art in zufälliger Reihenfolge ausführen
möchte - also nochmal "shuffle". Ich sehe nicht, wie ich mit einem
compare einerseits sortieren, andererseits zufällig mischen kann.

Ich denke wohl immer in Smalltalk-Ideomen und da wäre jeweils ein
Einzeiler. Und dann ärgere ich mich, dass ich in Java mehr Aufwand
habe, meine Ideen umzusetzen.

orders all: BuildOrder do: [...]

Collection>all: aClass do: aBlock
(self select: [:each | each isKindOf: aClass]) shuffled do: aBlock

In einer Sprache wie Haskell ginge nach noch etwas eleganter, weil ich
aus (isKindOf: aClass) direkt eine Funktion machen könnte, ohne den
expliziten Block.
Post by Wolfgang Eibner
Wenn schon würd ich die Typen bzw. kind als enum definieren und make als
ersten Parameter übergeben.
Enum kann Java erst ab 5.0 und Eclipse noch gar nicht. Klar könnte man
das sonst nehmen. Ich hatte ja schon mal voraussehend einfach Object
als key geschrieben. Da kann man dann nehmen, was immer man möchte.
Post by Wolfgang Eibner
PS: Ich freue mich dem großen SMA bei seinem List-Problem geholfen zu haben
;-)
Hey, auch ich weiss nicht alles - tatsächlich weiss ich manchmal
erschreckend wenig und freue mich, dann über jede Hilfe.
Post by Wolfgang Eibner
PPS: An welchem Weltraum-Shooter bastelst du eigentlich?
Ich hatte man ein simples Strategiespiel als Analyse und Design-Übung
geschrieben und nutze das seit einiger Zeit, um das "perfekte Design" zu
suchen. Soweit Eclipse es bereits kann versuche, nutze ich jetzt zum
Üben generische Typen. Das Spiel an sich ist wenig spektakulär und
zudem noch unausgewogen von den Regeln. Der erste Teil von
http://www.3plus4.de/stuff/wc.html beschreibt das Spiel.


bye
--
Stefan Matthias Aust // "Zweifel sind der Ansporn des Denkens..." -U
Wolfgang Eibner
2004-07-01 11:19:48 UTC
Permalink
Post by Stefan Matthias Aust
Post by Wolfgang Eibner
Hm, und warum nicht eine sortierte Collection nehmen und die Orders
Comparable implementieren lassen. So hättest du dann beim Ausführen schon
mal sicher die richtige Reihenfolge.
Weil ich alle Befehle einer Art in zufälliger Reihenfolge ausführen
möchte - also nochmal "shuffle". Ich sehe nicht, wie ich mit einem
compare einerseits sortieren, andererseits zufällig mischen kann.
Also ich schon, auch wenn dass Design dann net unbedingt mehr sooo schön
is...

z.B. wenn alle BuildOrders vor den CombatOrders sein sollten:

In BuildOrder:
int compareTo(Order o2) {
if(o2 instanecof CombatOrder)
return +100;
if(o2 instanceof BuildOrder)
return ((int) (Math.random()*10)-5);
}

In CombatOrder:
int compareTo(Order o2) {
if(o2 instanecof BuildOrder)
return -100;
if(o2 instanceof CombatOrder)
return ((int) (Math.random()*10)-5);
}

Es wird durch die Fixwerte +100 bzw. -100 sichergestellt, dass die
verschiedenen Arten getrennt sind, und durch die Zufallszahlen in dem
Bereich von -5 bis +5, dass die Orders einer Art gemischt sind. Nur bin ich
mir net sicher, ob dass so funktioniert, da ich nicht weiß wie Java genau
sortiert intern und ob es die Fixwerte mag.
Eventuell muss man vielleicht auch die 0 beim random() ausschließen.

Ich könnt mir aber auch nen Ansatz mit zwei Comperator die hintereinander
angewendet werden sollen vorstellen:
Der erste arbeitet nur wenn zwei verschiedene Orders herkommen und sortiert
sie. Der zweite arbeitet nur wenn zwei gleiche Orders herkommen und mischt
diese zufällig. Vielleicht gehts auch in einem... keine Ahnung.
Post by Stefan Matthias Aust
Post by Wolfgang Eibner
PS: Ich freue mich dem großen SMA bei seinem List-Problem geholfen zu haben
;-)
Hey, auch ich weiss nicht alles - tatsächlich weiss ich manchmal
erschreckend wenig und freue mich, dann über jede Hilfe.
Jep, aber weit mehr über andere Sprachen als ich. Daher lese ich deine
Beiträge immer recht gespannt.
Post by Stefan Matthias Aust
Post by Wolfgang Eibner
PPS: An welchem Weltraum-Shooter bastelst du eigentlich?
Ich hatte man ein simples Strategiespiel als Analyse und Design-Übung
geschrieben und nutze das seit einiger Zeit, um das "perfekte Design" zu
suchen. Soweit Eclipse es bereits kann versuche, nutze ich jetzt zum
Üben generische Typen. Das Spiel an sich ist wenig spektakulär und
zudem noch unausgewogen von den Regeln. Der erste Teil von
http://www.3plus4.de/stuff/wc.html beschreibt das Spiel.
Hm, ich werds mal lesen wenn ich Zeit hab...

mfg.
Wolfgang
Paul Ebermann
2004-07-01 19:10:18 UTC
Permalink
Post by Wolfgang Eibner
Post by Stefan Matthias Aust
Post by Wolfgang Eibner
Hm, und warum nicht eine sortierte Collection nehmen und die Orders
Comparable implementieren lassen. So hättest du dann beim Ausführen
schon mal sicher die richtige Reihenfolge.
Weil ich alle Befehle einer Art in zufälliger Reihenfolge ausführen
möchte - also nochmal "shuffle". Ich sehe nicht, wie ich mit einem
compare einerseits sortieren, andererseits zufällig mischen kann.
Also ich schon, auch wenn dass Design dann net unbedingt mehr sooo schön
is...
int compareTo(Order o2) {
if(o2 instanecof CombatOrder)
return +100;
if(o2 instanceof BuildOrder)
return ((int) (Math.random()*10)-5);
}
Das erfüllt nicht den Contract von compareTo():

| The implementor must ensure sgn(x.compareTo(y)) == -sgn(y.compareTo(x))
| for all x and y.

Um das hinzubekommen, müsstest du dir das Ergebnis
eines Vergleiches merken.

Auch die Transitivität geht verloren.

(Das heißt nicht, dass es nicht vielleicht
trotzdem funktionieren könnte.)
Post by Wolfgang Eibner
int compareTo(Order o2) {
if(o2 instanecof BuildOrder)
return -100;
if(o2 instanceof CombatOrder)
return ((int) (Math.random()*10)-5);
}
Es wird durch die Fixwerte +100 bzw. -100 sichergestellt, dass die
verschiedenen Arten getrennt sind, und durch die Zufallszahlen in dem
Bereich von -5 bis +5, dass die Orders einer Art gemischt sind. Nur bin ich
mir net sicher, ob dass so funktioniert, da ich nicht weiß wie Java genau
sortiert intern und ob es die Fixwerte mag.
Die Fixwerte sind kein Problem - es wird sowieso nur das Vorzeichen
des Ergebnisses betrachtet.
Post by Wolfgang Eibner
Eventuell muss man vielleicht auch die 0 beim random() ausschließen.
... und darauf achten, dass jedes Objekt zu sich
selbst gleich ist (x.compareTo(x) == 0).


Paul
Stefan Matthias Aust
2004-07-02 08:43:05 UTC
Permalink
Post by Wolfgang Eibner
int compareTo(Order o2) {
if(o2 instanecof BuildOrder)
return -100;
if(o2 instanceof CombatOrder)
return ((int) (Math.random()*10)-5);
}
Du musst dir die Zufallszahlen merken, sonst kann ich nicht für die
Sortierung garantieren, die natürlich jeden Vergleich mehrfach aufrufen
darf. Zudem ist auf diese Weise für mich nicht nachvollziehbar, ob das
Mischen gleichmäßig fair erfolgt, was bei dem von shuffle benutzen
Algorithus gegeben ist.


bye
--
Stefan Matthias Aust // "Zweifel sind der Ansporn des Denkens..." -U
Matthias Ernst
2004-07-01 07:14:53 UTC
Permalink
Post by Stefan Matthias Aust
Die Sprache Nice macht das so und ich begreife nicht, warum Java das
nicht auch so macht. Würde das lästige Konstrukt
if (x instanceof Y) {
Y y = (Y) x;
...
}
einsparen und bei 1.5 auch die doofe Warnung vermeiden...
Das hab ich mir einst mal bei Sun gewuenscht, a la

with(x instanceof Y) {
...
}

wurde aber abgelehnt, weil syntactic sugar und das macht man ja nicht.
Hab es leider im Zuge von Tiger versaeumt, mich nochmal darum zu
kuemmern.

Matthias
Stefan Matthias Aust
2004-07-01 07:19:56 UTC
Permalink
Post by Matthias Ernst
Das hab ich mir einst mal bei Sun gewuenscht, a la
with(x instanceof Y) {
...
}
wurde aber abgelehnt, weil syntactic sugar und das macht man ja nicht.
Ach, und for, while und do/while ist nicht auch alles nur syntaktischer
Zucker? Dummes Argument. Zudem, es ist IMHO kein neues Konstrukt
notwendig sondern man könnte das nach jedem instanceof-Test machen.
Post by Matthias Ernst
Hab es leider im Zuge von Tiger versaeumt, mich nochmal darum zu
kuemmern.
Naja, in zwei Jahren oder so gibt es dann ja Java 6.0, vielleicht hat
Sun dann ja ein einsehen mit den verbliebenen Java-Entwicklern :)


bye
--
Stefan Matthias Aust // "Zweifel sind der Ansporn des Denkens..." -U
Lesen Sie weiter auf narkive:
Loading...