ArrayList-iteration mit Prüfung auf instanceof durch switch case?

Alina_C

Mitglied
Hallo :meld:

Ich habe folgende Situation. Eine ArrayList kann verschiedene Arten von (, sagen wir mal, )Tieren aufnehmen. Nun sollen alle Methoden, die ein spezielles Tier (zb. Fisch mit schwimmen(); oder Vogel mit fliegen()😉 bereitstellt, getestet werden.

Ich denke normalerweiße würde man mit einer for Schleife durchiterieren, wobei jedesmal auf Fisch oder Vogel geprüft wird.
Java:
...
for (int x = 0; x < tierListe.size(); x++){
  if (tierListe.get(x) instanceof Vogel){
     tierListe.get(x).fliegen();
  }
  if (tierListe.get(x) instanceof Fisch){
     tierListe.get(x).schwimmen();
  }
}

...

Angenommen ich habe jetzt aber nicht nur diese zwei Arten von Tieren, sondern 10/20/oder gar 50 -
ich habe gehört es sei sinnvoller auf switch case zu setzen, wenn es viele Fallunterscheidungen gibt.
Aber wieviele sind "viele"? Und gilt das auch für ArrayLists mit instanceof-Operator in jeder case-Klausel?
Gibt es eine bessere Lösung als isntanceof? (Ich habe gelesen, dass die Benutzung davon nicht gut für die Performance sei) Wie wäre es mit if -Anweisungen in der Art
Code:
if(tierListe.get(x).getClass()=="Vogel")


::gerade bemerkt, dass getClass in diesem Fall nur class arrayListClasses zurückgibt :noe:
 
verwende eine gemeinsame Basisklasse, und sei es ein Wrapper der auf andere Objekte verweist,
und ordne jedem Objekt je nach Klasse einen bestimmten Wert eines Enums zu, dann geht auch switch leicht:
Java:
switch(tier.getType()) {
  case Vogel:
    ..
    break;
  case Fisch:
    ..
    break;

}
für tierspezifische Methoden muss man dann normalerweise casten,
haben bei dir alle Tiere die Methoden fliegen();, schwimmen(); usw?

---

besonders schlimm ist aber noch tierListe.get(x) 4x in deinem Code oder noch öfter zu schreiben,
speichere tierListe.get(x) in einer Variable tier in der Schleife und verwende dann überall tier

---

das Class-Objekt müsstest du mit Vogel.class usw. vergleichen,
die Klasse ArrayList sollte beim geposteten Code nicht erscheinen
 
[c]switch[/c] kann keine Typunterscheidung vornehmen. Dein Code würde übrigens nicht funktionieren, er müsste ungefähr so aussehen:

Java:
...
for (int x = 0; x < tierListe.size(); x++){
  Tier tier = tierListe.get(x);
  if (tier instanceof Vogel){
     ((Vogel) tier).fliegen();
  }
  if (tier instanceof Fisch){
     ((Fisch) tier).schwimmen();
  }
}
...

Eleganter ist übrigens die "enhanced for-loop":

Java:
...
for (Tier tier : tierListe){
...

Wie man das Problem in der Praxis löst, hängt von den genauen Einsatzbedingungen ab. Oft liegt ein Designfehler vor, wenn du so eine Konstruktion mit mehr als drei, vier Unterklassen hast.


[edit]
Slaters Lösung gefällt mir nicht so toll. Den Typ einer Objekts kann ich mit
Code:
getClass()
ermitteln oder mit
Code:
instanceof
testen. Ein zusätzliches Enum-Feld dupliziert sozusagen die Typinformationen, und das ist in der Regel keine gute Sache. Es mag Anwendungsfälle geben, wo diese Technik ihre Berechtigung hat, im Allgemeinen würde ich versuchen, so etwas zu vermeinden.
[/edit]
 
Zuletzt bearbeitet:
Vielen Dank für die wertvollen Tipps.
Typecasting hatte ich vergessen, denn hier soll nicht jedes Tier fliegen/schwimmen können.

Landei, dürfte ich dich bitten etwas genauer auf den möglichen Designfehler einzugehen?
Jedes Objekt {erbt|implementiert} doch {von der|die} Oberklasse Tier.
Sollte man Tier noch weiter gliedern? E.g. in Säugetiere, Reptilien, Amphibien, Wirbeltiere...
Bin keine Biologin, aber es könnte doch auch den Fall geben, dass ein Tier ein Vogel und ein Saurier (sorry, mir fiel nichts ordentliches ein) ist...
Gibt es spezielle Bücher über solche Designfragen?
 
worin unterscheiden sich denn die 20 Tiere und mehr, brauchen die wirklich alle eine eigene Klasse, hat jeder individuelle Methoden?
man sieht ja auch an deinen if/else oder switch-Aufrufen wie unhandlich das ist,

statt z.B.
fliege(), schwimme(), gehe(), schleiche(), moonwalke() usw. reicht vielleicht generell eine Methode bewege(), die in allen Unterklassen passend befüllt ist, ob nun geflogen oder geschwommen wird,
das macht den Aufruf extrem komfortabler, die Unterscheidung fällt komplett weg,
unabhängig davon müssen es nicht vielleicht nicht so viele Klassen sein oder zumindest gemeinsame Mittelklassen wie eben für alle Flieger

oder noch eine Variante sind nur gewisse Klassen wie Flieger, die für verschiedene theoretische Endklassen genutzt werden,
Vögel, Drachen und fliegende Tomaten sind keine eigenen Klassen sondern Flieger-Objekte mit interner Konfiguration,
gerade dann auch bietet sich Enum zur internen Unterscheidung an, wenn die Klasse es selber nicht mehr her gibt,

aber meiner Meinung nach ist das auch bei einzelnen Klassen nicht zu verachten, allein um instanceof oder noch andere Aktionen mit den Class-Objekten zu vermeiden, etwa Key in einer Map, mit Enum kann man viel anstellen
 
Zuletzt bearbeitet von einem Moderator:
statt z.B.
fliege(), schwimme(), gehe(), schleiche(), moonwalke() usw. reicht vielleicht generell eine Methode bewege(), die in allen Unterklassen passend befüllt ist, ob nun geflogen oder geschwommen wird,

Sehe ich genauso, würde dann auch eher sowas nehmen wie:

Java:
public class Tier {

     public void bewegen() {
          //Todo: Nothing
     }

}

Java:
public class Vogel extends Tier {

     @Override
     public void bewegen() {
          System.out.println("Ich flieeeeeege!");
     }

}
 
Hi, stell dir mal folgendes vor:

  • 1. Nehmen als Tier den Papageientaucher. Kann fliegen, schwimmen und tauchen. Kein Fisch.
  • 2. Nehmen als Tier den Pinuin. Kann schwimmen, tauchen aber nicht fliegen. Kein Fisch.
  • 3. Nehmen als Tier die Fledermaus/den Flughund. Kann fliegen. Kein Vogel.
  • 4. Nehmen als Tier den "fliegenden Fisch"(kann nicht wirklich fliegen, nur weit aus dem Wasser hüpfen, aber egal). Kann tauchen,schwimmen und fliegen. Kein Vogel

Wie würdest du das jetzt mit dieser Sicht angehen? Abstrakt denken dann programmieren.

Edit: Die Herren haben nun das vorweg genommen, wo ich dich durch nachdenken hinkriegen wollte. Also ignorier es
 
Hi, stell dir mal folgendes vor:

  • 1. Nehmen als Tier den Papageientaucher. Kann fliegen, schwimmen und tauchen. Kein Fisch.

Java:
public class Tier {

     protected boolean fly;
     protected boolean swim;
     protected boolean dive;
     protected boolean run;

     public Tier() {
          fly = false;
          swim = false;
          dive = false;
          run = false;
     }

     public void move() {
          //Todo: Nothing
     }

     public boolean canFly() {
          return fly;
     }

     public boolean canSwim() {
          return swim;
     }

     public boolean canDive() {
          return dive;
     }

     public boolean canRun() { //oder canWalk
          return run;
     }

}

Java:
public class Vogel extends Tier {

     public Vogel() {
          super();
          fly = true;
     }

     @Override
     public void move() {
          System.out.println("Ich flieeeeeege!");
     }

     

}


Java:
public class Papageientaucher extends Vogel {

     public Papageientaucher() {
          super();
          swim = true;
          dive = true;
     }

     @Override
     public void move() {
          System.out.println("Ich tauschschwimfliege!");
     }

}

PS: Steinigt mich wenns falsch is...

PPS:
Testklasse:
Java:
public class Test {

	public static void main(final String[] args) {
		List<Tier> tierList = new ArrayList<Tier>();
		Papageientaucher ptauch = new Papageientaucher();
		Vogel vogel = new Vogel();
		tierList.add(ptauch);
		tierList.add(vogel);
		for (Tier tier : tierList) {
			tier.move();
		}
	}

}
 
Zuletzt bearbeitet:
Um noch mal zur Ursprungsfrage zurück zu kommen: In Java 7 gehen jetzt auch Strings als Bedingung für switch/case. Du könntest also mit getClass().getName() den Klassennamen ermitteln und darüber dann switchen.

Ansonsten ist Code, der so arbeitet sehr schlecht wart-/erweiterbar. Immer, wenn eine neue Klasse hinzukommt, musst du nämlich dran denken, an einer bestimmten Stelle Deinen Code anzupassen. In den meisten Fällen lässt sich solch ein Code mit einem besseren Design umgehen:
- Besser durchdachte Klassenhirarchie mit (abstrakten) Basisklassen
- Möglicherweise viele schmale Interfaces implementieren, statt von einer mächtig überfrachteten Basisklasse ableiten
- Vielleicht sogar auf Vererbung verzichten und mit Komposition arbeiten.
 
@Gossi: Wer ohne Fehler ist, werfe den ersten Stein. Ich bin zwar nicht ohne Fehler, werfe jetzt aber trotzdem mal 😉.
In Deinem Post von heute 14:53 wäre wohl statt der leeren und relativ sinnfreien default-Implementierung der Methode bewegen eine abstrakte Basisklasse Tier mit abstrakter Methode bewegen besser gewesen, ungefähr so:
Java:
public abstract class Tier {
 
     public abstract void bewegen();

     .... 
}
Und Deine Lösung zu knucki's Anregungen ist grauenhaft. Die Methoden haben in Tier nix zu suchen und sollten besser mit Interfaces (Flyer#fly, Swimmer#swimm, Walker#walk) definiert werden. Ein fliegender Fisch würde dann bspw. Flyer und Swimmer implementieren.
 
Zuletzt bearbeitet von einem Moderator:
als Alternative zu den booleans von Gossi bietet sich auch hier einmal mehr Enums an,
jedes Objekt hat ein Set beliebig kombinierbarer Eigenschaften

andererseits natürlich auch nicht ganz schön, wenn man 5 Papageientaucher und 4 Pinguins hat,
genau darauf zu achten, wer welche Eigenschaften hat, nicht dass sich da etwas unterscheidet,

eigene Subklassen durchaus denkbar, wenn ohne neue Methoden, nur interne Konfiguration,
ohne nötiges switch, dann ja auch nicht ganz so gefährlich

falls aber Papageientaucher nur von einer bestimmten Methode erzeugt werden, ginge es auch ohne eigene Klasse
 
Ich weiss, für das wird mich nillehammer schlagen, aber des Verständnisses wegen, habe ich eine kleine Impl ohne Interfaces gemacht:

Java:
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class Tier {
  public final int               walk     = 0;
  public final int               crouch   = 1;
  public final int               run      = 2;
  public final int               swim     = 3;
  public final int               dive     = 4;
  public final int               fly      = 5;
  public final int               moonwalk = 6;

  private final Set<Integer>     moves    = new HashSet<Integer>();

  protected Map<Integer, String> movesX   = new HashMap<Integer, String>();

  public Tier() {
    movesX.put(walk, "gehen");
    movesX.put(crouch, "kriechen");
    movesX.put(run, "rennen");
    movesX.put(swim, "schwimmen");
    movesX.put(dive, "tauchen");
    movesX.put(fly, "fliegen");
    movesX.put(moonwalk, "Auf dem Mond laufen ^^");
  }

  public void addMove(final int move) {
    moves.add(move);
  }

  public Set<Integer> moving() {
    return moves;
  }

  public String movingX() {
    if (moves.isEmpty()) {
      return "Kann gar nix!";
    }
    final StringBuilder sb = new StringBuilder();
    sb.append("Ich kann: ");
    for (final int move : moves) {
      sb.append(movesX.get(move) + ", ");
    }
    return sb.substring(0, sb.lastIndexOf(","));
  }
}
Java:
public class Papageientaucher extends Tier {

  public Papageientaucher() {
    super();
    addMove(walk);
    addMove(crouch);
    addMove(swim);
    addMove(dive);
    addMove(fly);
  }

}

Java:
public class MoonwalkingTeletubbie extends Tier {
  public MoonwalkingTeletubbie() {
    super();
    addMove(walk);
    addMove(moonwalk);
  }
}
Java:
import java.util.ArrayList;
import java.util.List;

public class IchbineinTier {

  /**
   * @param args
   */
  public static void main(final String[] args) {
    final List<Tier> tiere = new ArrayList<Tier>();
    final Papageientaucher p = new Papageientaucher();
    tiere.add(p);
    final MoonwalkingTeletubbie m = new MoonwalkingTeletubbie();
    tiere.add(m);
    for (final Tier tier : tiere) {
      System.out.println(tier.movingX() + " und bin ein " + tier.getClass().getName());
    }

  }
}

Zu beachten ist, dass die Basisklasse maximal das implemetiert, was für Alle abgeleiteten Klassen gilt!

Nille darfst draufhauen 😀
 
Wenn Dich jemand schlägt, halte auch die andere Wange hin (hab's heut mit der Bibel).
knucki hat gesagt.:
Java:
  public final int               walk     = 0;
  public final int               crouch   = 1;
  public final int               run      = 2;
  public final int               swim     = 3;
  public final int               dive     = 4;
  public final int               fly      = 5;
  public final int               moonwalk = 6;
 
  private final Set<Integer>     moves    = new HashSet<Integer>();
Wenn schon so, dann doch lieber -wie von SlaterB vorgeschlagen- mit enum und EnumSet.
 

Neue Themen


Zurück
Oben