[Java] Im Klassen-Array Methoden untergeordneter Klassen aufrufen

Gismodin

Cadet 3rd Year
Registriert
Mai 2013
Beiträge
32
Hallo,

mit folgendem Programm wird ein Array der Klasse Lebewesen erstellt, welches mit den untergeordneten Klassen Mensch/Hund befüllt wird.
Anschließend soll das Array durchgegangen werden und jeweils klassenspezifisch eine Methode aufrufen:

Code:
public class Project {
	public static void main(String[] args) {
		Lebewesen[] lebewesen = new Lebewesen[5];
		Scanner s = new Scanner(System.in);
		String eingabe = "";
		int i = 0;
		
		while(i < lebewesen.length) {
			 System.out.println("Welches Lebewesen soll erstellt werden? (mensch/hund/ende)");
			 eingabe = s.next();
			 if(eingabe.equals("hund")) {
				 lebewesen[i] = new Hund();
				 i++;
			 }else if(eingabe.equals("mensch")) {
				 lebewesen[i] = new Mensch();
				 i++;
			 }else if(eingabe.equals("ende")) {
				 i = lebewesen.length;
			 }else {
				 System.out.println("Ungültige Eingabe!");
			 }
		 }
		for(int x = 0; x < lebewesen.length; x++) {
			if(lebewesen[x] instanceof Mensch) {
				System.out.println("BlaBla!");
//				lebewesen[x].reden();
				System.out.println("" + lebewesen[x].getHaarfarbe());
			}else if(lebewesen[x] instanceof Hund) {
				System.out.println("WauWau!");
			}else {
				System.out.println("leer");
			}
		}
		
	}
}

Der auskommentierte Befehl lebewesen[x].reden(); ist aber nicht möglich, da Lebewesen keine Methode reden haben. Menschen aber schon, nur kann er das nicht erkennen.

Wie kann ich auf Methoden der Sub-Klasse zugreifen? (getHaarfarbe ist Methode der Lebewesen-Klasse)

Gruß

Gismodin
 
Erstelle dir ein Mensch Objekt nach deiner If instance of abfrage. Das kannst du reden lassen. (Habe jetzt nur ganz flüchtig geschaut)

Bisher geht es bei dir nicht, weil ja erst zur Laufzeit klar wird, um welches Lebewesen es sich handelt.
 
Code:
((Mensch)lebewesen[x]).reden();

Das sollte funktionieren.
 
Code:
((Mensch)Lebewesen[x]).reden();

Du musst das Object im Array zu einem Mensch casten.
Code:
(Mensch)Lebewesen[x]
.

Um dann auf eine funktion zugreifen zu können musst du das noch einmal umklammern.
Code:
((Mensch)Lebewesen[x])

und dann die funktion.
 
Zuletzt bearbeitet:
Schau dir instanceof an, also etwa so:

if (lebewesen[x] instanceof Mensch)
{
Mensch mensch = (Mensch) lebenwesen[x];
mensch.reden();
}

Lebewesen ist nur ein Interface. Mensch und Hund implementieren das Interface Lebewesen.
Wenn du dann mit dem Interface hantierst, dann stehen dir lediglich die Methoden aus dem Interface zur Verfügung. Für die Methoden aus Mensch bzw. Hund musst du immer casten. Mit instanceof kannst du feststellen, ob es sich um Mensch oder Hund handelt.
 
Zuletzt bearbeitet:
Danke für die schnellen und zahlreichen Antworten

Raggygandalf schrieb:
Code:
((Mensch)Lebewesen[x]).reden();

Du musst das Object im Array zu einem Mensch casten.
Code:
(Mensch)Lebewesen[x]
.

Um dann auf eine funktion zugreifen zu können musst du das noch einmal umklammern.
Code:
((Mensch)Lebewesen[x])

und dann die funktion.

Ich habe jetzt diese Lösung versucht und das hat auch sofort geklappt. Zudem habe ich noch die Variable int iq der Klasse Mensch erhöht, um zu prüfen, was passiert, wenn mehrere Menschen erstellt werden

Code:
public class Polymorphie {
	public static void main(String[] args) {
		Lebewesen[] lebewesen = new Lebewesen[5];
		Scanner s = new Scanner(System.in);
		String eingabe = "";
		int i = 0;
		int a = 10;
		
		while(i < lebewesen.length) {
			 System.out.println("Welches Lebewesen soll erstellt werden? (mensch/hund/ende)");
			 eingabe = s.next();
			 if(eingabe.equals("hund")) {
				 lebewesen[i] = new Hund();
				 i++;
			 }else if(eingabe.equals("mensch")) {
				 lebewesen[i] = new Mensch();
				 i++;
			 }else if(eingabe.equals("ende")) {
				 i = lebewesen.length;
			 }else {
				 System.out.println("Ungültige Eingabe!");
			 }
		 }
		
		for(int x = 0; x < lebewesen.length; x++) {
			if(lebewesen[x] instanceof Mensch) {
				System.out.println("BlaBla!");
				((Mensch)lebewesen[x]).reden();
				((Mensch)lebewesen[x]).setIQ(a);
				a = a + 5;
			}else if(lebewesen[x] instanceof Hund) {
				System.out.println("WauWau!");
			}else {
				System.out.println("leer");
			}
		}
		System.out.println("" + ((Mensch)lebewesen[0]).getIQ());
		System.out.println("" + ((Mensch)lebewesen[1]).getIQ());
		
	}
}

Die andere Lösung verstehe ich nicht ganz:

Code:
Mensch mensch = lebewesen[x]; // Mensch mensch = (Mensch)lebewesen[x];
mensch.reden();

Würde hier nicht in jedem Arrayfeld, dass einen Menschen beherbergen soll, der gleiche Mensch "mensch" gespeichert werden? Wenn ich falsch liege, was genau passiert dann in Zeile 1?

Danke für eure Antworten!
 
Du nimmst ja nur das vorhandene Objekt aus dem Array heraus. Es ist ja schon ein neues erstellt. Du packst es nur noch einmal in eine neue variable
 
Code:
		for(int x = 0; x < lebewesen.length; x++) {
			if(lebewesen[x] instanceof Mensch) {
				((Mensch)lebewesen[x]).reden();
				((Mensch)lebewesen[x]).setIQ(a);
				System.out.println("IQ: " + ((Mensch)lebewesen[x]).getIQ());
				a = a + 5;
			}else if(lebewesen[x] instanceof Hund) {
				Hund hund = (Hund)lebewesen[x];
				hund.bellen();
				hund.setBaelle(i);
				System.out.println("Anzahl Bälle: " + hund.getBaelle());
				i++;
			}else {
				System.out.println("leer");
			}
		}
		for(int x = 0; x < lebewesen.length; x++) {
			if(lebewesen[x] instanceof Mensch) {
				System.out.println("Mensch " + x + " hat einen IQ von " + ((Mensch)lebewesen[x]).getIQ());
			}else if(lebewesen[x] instanceof Hund) {
				System.out.printf("Hund %d hat %d Bälle", x, ((Hund)lebewesen[x]).getBaelle());
				System.out.println();
			}else {
				System.out.println("leer");
			}
		}

So jetzt habe ich mal beide Möglichkeiten verwendet. Scheinbar funktionieren auch beide gleich und sind über ((Mensch/Hund).lebewesen[x] ansteuerbar und geben die gewünschten Variablen passend aus.

Das lokale Objekt "hund" kann ich ja nur in der for-Schleife aufrufen, in der es erstellt wird, also muss ich in der zweiten for-Schleife ebenfalls auf den cast-Verweis zugreifen, oder?

Erspart Möglichkeit 2 (else if) lediglich Schreibarbeit, oder erfüllt sie noch einen praktischen Nutzen, den die verwendete Möglichkeit bei 'Mensch' (if) nicht hat?
 
Bei Möglichkeit 2 ersparst Du Dir einen Cast, das bringt in Zweifel mehr Performance. Wird in dem Fall aber nicht messbar sein, und der Compiler wird den Doppelcast auch höchstwahrscheinlich wegoptimieren, genau weiß ich es aber nicht.
 
Ist das für ein Schulprojekt? Wenn ja hat der Lehrer ein Händchen für sinnvolle Aufgaben.:D

Ne mal im Ernst, was du da geschrieben hast geht in Richtung Reflection und ist häufig ein Zeichen für ein unsolides Softwaredesign. In diesem Fall sieht das ganze jedenfalls ziemlich unsinnig aus. Du müsstest ja jede Subklassen-Instanz von Lebewesen in deiner if-else-Abfrage prüfen bevor du bestimmte Methoden aufrufst. Und wieso ist "getHaarfarbe" eine Methode der Lebewesenklasse? Was würde diese denn bei einer Schlange zurückgeben?;)

Imo wäre ein bestes Beispiel für die Verwendung von instanceof das Errorhandling.
 
Zuletzt bearbeitet:
@Drexel: Danke, das heißt also, dass Casts mehr Aufwand erzeugen? Würde also danach noch weitere Eingaben folgen, würde es sich Performancetechnisch lohnen, auf Casts zu verzichten und das Objekt lokal "zu erstellen"?

@G00fY
Ne, das is kein Schulprojekt. Bring mir Java gerade über ein Youtube-Tutorial bei, das eben so vor geht. Ich bin mir sicher, dass es dafür eine bessere Lösung gibt, nur steh ich ja gerade noch am Anfang ;)
Wenn du aber eine bessere Lösung hast, wie man die einzelnen Objekte des Arrays auslesen kann und dazu ein paar Kommentare hast, würde ich mir das natürlich gern ansehen.
 
Gismodin schrieb:
Ne, das is kein Schulprojekt. Bring mir Java gerade über ein Youtube-Tutorial bei, das eben so vor geht. Ich bin mir sicher, dass es dafür eine bessere Lösung gibt, nur steh ich ja gerade noch am Anfang ;).
Naja, bei dieser Youtube Aufgabe könnte man auch einfach ein Array von Objets erzeugen (von dem erben alle Klassen) und dann jeweils mittels instanceof prüfen ob es diese oder jene bekannte Klasse ist. Das Beispiel mit den Lebewesen, was vermutlich das Konzept der Vererbung näher bringen soll, ist jedenfalls so ziemlich ungeeignet und vermittelt grundsätzliche falsche Softwarekonzepte!

Finde ich super und löblich sich aus eigener Motivation mit so einem Thema zu befassen. Nur befürchte ich, wenn die restlichen Aufgaben in diesem Kurs ein ähnliches Niveau haben, tust du dir keinen wirklichen Gefallen damit.
Haste interessehalber mal den Link?


wahli schrieb:
Lebewesen ist nur ein Interface. Mensch und Hund implementieren das Interface Lebewesen.
Wenn du dann mit dem Interface hantierst, dann stehen dir lediglich die Methoden aus dem Interface zur Verfügung. Für die Methoden aus Mensch bzw. Hund musst du immer casten. Mit instanceof kannst du feststellen, ob es sich um Mensch oder Hund handelt.
Du wirfst hier übrigens Interfaces und abstrakte Klassen (Vererbung) durcheinander. Interfaces definieren lediglich Methoden, können diese aber niemals implementieren. Wie Gismodin das im ersten Post beschreibt muss Lebewesen eine abstrakte Klasse sein, welche u.a. die Methode getHaarfarbe implementiert. Er spricht ja weiter auch von "untergeordneten Klassen" Mensch und Hund. Das ist ein riesen Unterschied.

Raggygandalf schrieb:
Du nimmst ja nur das vorhandene Objekt aus dem Array heraus. Es ist ja schon ein neues erstellt. Du packst es nur noch einmal in eine neue variable
Hier muss man aufpassen! Es wir kein neues Objekt erstellt, sondern die Objekt-Referenz aus dem Array (bei Java stecken in einem Array Referenzen zu Objekten, keine Objekte selbst) in einer lokalen Variablen gespeichert (auch Variablen sind demnach nie Objekte sondern nur Referenzen auf Objekte im Heap oder null). Dieses grundlegende Konzept MUSS man unter Java möglichst früh begreifen (erspart einem viele Probleme :D). Hierzu zum Einlesen: Mit Referenzen arbeiten, Identität und Gleichheit

Das beantwortet somit zum teil also auch diese Frage.
Gismodin schrieb:
Würde also danach noch weitere Eingaben folgen, würde es sich Performancetechnisch lohnen, auf Casts zu verzichten und das Objekt lokal "zu erstellen"?
Ansonsten ist die Performance vom Operator instanceof oder dem expliziten Casten absolut vernachlässigbar (sie benötigen theoretisch zur Laufzeit zusätzliche Rechenschritte). Viel wichtiger bzw. alarmierender ist, wenn du deinen Code so designst, dass du mehrfach darauf zurückgreifen musst.;)
 
Zuletzt bearbeitet:
G00fY schrieb:
Finde ich super und löblich sich aus eigener Motivation mit so einem Thema zu befassen. Nur befürchte ich, wenn die restlichen Aufgaben in diesem Kurs ein ähnliches Niveau haben, tust du dir keinen wirklichen Gefallen damit.
Haste interessehalber mal den Link?

Und er ist schon viel weiter als HerrDrachen oder andere Spezialisten hier. ;)

G00fY schrieb:
Ansonsten ist die Performance vom Operator instanceof oder dem expliziten Casten absolut vernachlässigbar (sie benötigen theoretisch zur Laufzeit zusätzliche Rechenschritte). Viel wichtiger bzw. alarmierender ist, wenn du deinen Code so designst, dass du mehrfach darauf zurückgreifen musst.;)

Moderne Compiler optimieren wie gesagt auch schon sehr viel weg, aber es kann ungünstige Situationen geben, wo sowas nicht erkannt wird oder möglich ist und wirklich Performance kostet, wenn es massenhaft vorkommt, kommt immer ganz drauf an, was man für Software schreibt.

Ansonsten deutet diese vielfachen If Abfragen mit instanceof schon darauf hin, dass mit der Klassen-/Codestruktur was im argen liegt, aber ich denke um das Prinzip mal zu verstehen, war es ganz ok.
 
G00fY schrieb:
Interfaces definieren lediglich Methoden, können diese aber niemals implementieren.

Stimmt mit Java 8 nicht mehr. Seitdem gibt es die Möglichkeit der Default Methods.
 
G00fY schrieb:
Du wirfst hier übrigens Interfaces und abstrakte Klassen (Vererbung) durcheinander. Interfaces definieren lediglich Methoden, können diese aber niemals implementieren. Wie Gismodin das im ersten Post beschreibt muss Lebewesen eine abstrakte Klasse sein, welche u.a. die Methode getHaarfarbe implementiert. Er spricht ja weiter auch von "untergeordneten Klassen" Mensch und Hund. Das ist ein riesen Unterschied.

Jein.
Lebewesen könnte schon ein Interface sein, allerdings schreibt der TE was von Subklassen.
Wenn Lebewesen ein Interface wäre, dann muss natürlich die Implementierung der Methode in allen Verwenderklassen (Mensch, Hund) gemacht werden.
Ergänzung ()

G00fY schrieb:
Ne mal im Ernst, was du da geschrieben hast geht in Richtung Reflection und ist häufig ein Zeichen für ein unsolides Softwaredesign. In diesem Fall sieht das ganze jedenfalls ziemlich unsinnig aus. Du müsstest ja jede Subklassen-Instanz von Lebewesen in deiner if-else-Abfrage prüfen bevor du bestimmte Methoden aufrufst. Und wieso ist "getHaarfarbe" eine Methode der Lebewesenklasse? Was würde diese denn bei einer Schlange zurückgeben?;)

Reflection wird auch im Profibereich verwendet. Grundsätzlich sollte man davon aber Abstand halten, weil es nicht Refactoringsafe ist und zur Laufzeit evtl. schlimme Dinge passieren können. Da muss man wissen, was man tut.

In einer meiner Projekte (Umfang 100 Mannjahre) wird auch eine Liste mit unterschiedlichen Objekten gehalten (vergleichbar mit Mensch und Hund), damit man flexibel bleibt und jederzeit ein weiteres neues Objekt in die Liste aufnehmen kann, welches aktuell noch nicht bekannt ist. Wir haben ein Interface definiert, welches alle diese Objekte implementieren müssen (vergleichbar mit Lebewesen). Da gibt es lediglich ein paar verschiedene Methodendefinitionen (vergleichbar mit getHaarfarbe). Innerhalb der Objekte sehen die Inhalte aber völlig unterschiedlich aus und werden auch separat mit verschiedenen GUI-Masken editiert/angezeigt. Da wird natürlich viel mit instanceof gearbeitet. Natürlich ist alles mit Unittests bestens abgesichert.
In dem Projekt ist ein Team nur mit diesen Objekten zuständig (inkl. GUI-Masken) und ein anderes Team verwendet nur das Interface um eine saubere Trennung auch auf Modulebene zu erreichen.
Ich könnte jetzt auch noch erklären, wie das in der DB gehandhabt wird, aber das geht jetzt zu weit ;-)
 
Zuletzt bearbeitet:
Wenn ich den Code so lese ist neben den Designproblemen, die schon angesprochen wurden, auch noch eine Sache anzumerken: Gewöhne dir von Anfang an direkt an sprechende Variablennamen zu verwenden. Wenn ich Deklarationen, wie int i und int a sehe, dann mögen die zwar als Zählvariable in einer for-Schleife gerechtfertigt sein, aber sobald diese außerhalb einer Schleife stehen, fängt mein Puls an zu rasen. Ganz im Sinne von Robert C. Martin muss der Code selbsterklärend sein, vor allem die Bezeichung von Operanden, welche Variablen sein können!
 
Selbst in einer Zählschleife ist das voll nervig. Such mal nach i wo der Zähler überall verwendet wird.... Da würde ich wenigstens index schreiben.
 
wiztm schrieb:
Welcher Editor nutzt den keine Syntaxhervorherung etc?
:confused_alt:

Ich befürchte, Du erkennst den Punkt nicht. Bei Code, der wie der oben gezeigte aussieht, ist das trivial. Wenn Du aber 3-fach geschachtelte Rekursionen hast, bringt dir Dein Syntax-Highlightning niente, nada, nothing! Und jetzt noch Variablennamen, die a, b, c, i, r, x und y heißen, dann bist Du bedient, promised!
 
Zurück
Oben