Klassen als Datentyp nutzen?

Diskutiere Klassen als Datentyp nutzen? im Java Basics - Anfänger-Themen Bereich.
I

iRecordS

Hallo liebe Community,

ich bin gerade dabei die Grundlagen von Java zu lernen. Dabei bin ich auf etwas gestoßen, was ich mir nicht vorstellen kann. Und zwar geht es dabei darum, dass ich einmal die Klasse Tier habe und einmal die Klasse Hund.

Code:
public class Tier {
    private String name;
    private int Alter;
    private String Farbe;
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    .
    .
    .
}
Code:
public class Hund extends Tier {
    private int hundeMarke;
    
    public void getHundeMarke() {
        return hundeMarke;
    }
    
    public int setHundeMarke(int hundeMarke) {
        this.hundeMarke = hundeMarke;
    }
}
Wenn ich jetzt in der Main Klasse ein Objekt von Hund erzeuge, würde ich das so angehen:
Hund meinHund = new Hund();

Allerdings habe ich auch gesehen, dass so etwas möglich ist:
Tier meinHund = new Hund();

Wie soll ich mir das jetzt vorstellen? Welchen Unterschied macht es und in welchen Fällen brauche ich das?

Mfg
iRecordS
 
L

LimDul

Das ist einer der Grundpfeiler der Vererbung und Co.
Lektüre: http://openbook.rheinwerk-verlag.de/javainsel9/javainsel_05_008.htm#mjf275cae4069cb07f60b602bcbea532c8

Angenommen, du hast irgendwo folgende Methode

Java:
public void gibNamenAus(Tier tier) {
  System.out.println(tier.getName());
}
Diese Methode soll natürlich auch funktionieren, wenn du einen Hund reinsteckst.

Jedes Objekt vom Typ Hund ist ja auch gleichzeitig ein Objekt vom Typ Tier. Den Hund ist eine Spezialisierung von Tier.

Wenn du also Tier meinHund = new Hund(); machst, dann erzeugt du zwar ein Objekt vom Typ Hund, weißt es aber einer Variablen von Typ tier zu. Das heißt der Compiler kann nun nur auf die Methoden von der Klasse Tier zugreifen - da der Compiler nicht erkennen kann, was wirklich drin steckt. Das wird erst zur Laufzeit ausgewertet.

z.B. wird dies gerne bei Listen verwendet:

Java:
List<String> meineListe = new ArrayList<>();
Für die weitere Nutzung ist es nicht relevant das es eine ArrayList ist. Wenn du also später dich z.B. mal entscheidest anstelle der ArrayList eine LinkedList zu verwenden, dann musst du nur diese Zeile ändern. Würdest du stattdessen schreiben

Java:
ArrayList<String> meineListe = new ArrayList<>();
und willst es dann austauschen, dann kann es sein dass irgendwo zig Zeilen später jemand eine Methode genutzt hat, die es nur in der ArrayList gibt - und dann kannst die Implementierung nicht mehr austauschen.
 
I

iRecordS

@LimDul
Wenn du also Tier meinHund = new Hund(); machst, dann erzeugt du zwar ein Objekt vom Typ Hund, weißt es aber einer Variablen von Typ tier zu. Das heißt der Compiler kann nun nur auf die Methoden von der Klasse Tier zugreifen - da der Compiler nicht erkennen kann, was wirklich drin steckt. Das wird erst zur Laufzeit ausgewertet.
Das verstehe ich jetzt aber nicht ganz. Wie ist das gemeint, das der Compiler das nicht erkennt? Kann ich denn jetzt immernoch auf die Methoden von der Klasse Hund und der Klasse Tier zugreifen? Und warum sollte ich dann nicht immer einfach die Superklasse nehmen, wenn ich ein Objekt einer Kindsklasse erzeuge?
 
J

JustNobody

Wenn Du eine Variable vom Typ Tier hast, dann kann kannst Du da alles rein stecken, das "ein Tier ist" (Das ist etwas blöd formuliert. Das sich wie ein Tier verhält ist besser, aber irgendwie hat sich die andere Ausdrucksweise durchgesetzt), also Hund, Katze, ....

Aber du kannst nur auf das Verhalten von Tier zugreifen. Also kein Bellen oder Miauen ... Denn aus Sicht des Compilers könnte da ja eine Katze drin sein, dann wäre kein Bellen möglich. Oder es könnte ein Hund sein, dann wäre das Miauen nicht möglich.
Der Compiler kann nur auf das Verhalten zurück greifen, das ein Tier bietet. Also z.B. "getName" aufrufen.

Wenn Du ein weitergehendes Verhalten willst, dann musst Du erst prüfen, ob der Typ stimmig ist:
Ist es ein Hund? -> Auf das Verhalten des Hundes zugreifen.
Ist es eine Katze? -> Auf das Verhalten der Katze zugreifen.

Dann gibt es aber auch Interfaces. So ist ein Interface denkbar für Tiere, die Ihre Temperatur regeln. Hunde und Katzen haben das (alle Säugetiere). Eine Schlange reguliert die eigene Temperatur aber nicht, da braucht man das nicht.

Ein Tierarzt kann also auch fragen:
-> Hat es das Verhalten "TemperaturReglung"? -> getTemperatur, getMinNormalTemperature, getMaxNormalTemperature -->Damit können wir die Temperatur kontrollieren.

Und daher nimmst Du immer den Typ, den du brauchst.
Wenn Du ein Gerät entwickelst, das die Temperatur auswertet, dann nimmt das kein Tier, Hund oder Katze entgegen, sondern einfach nur ein "TemperaturReglung". Denn das kann die Temperatur prüfen, sobald dieses Interface implementiert wurde. Es muss die Klassen gar nicht kennen, die das implementieren.
Das Gerät funktioniert also ganz Universell! Du kannst mit dem Gerät auch prüfen, ob die Temperatur von einer CPU ok ist oder ob ein Backofen die richtige Temperatur hat. Es ist unabhängig von dem Tier. ==> Wiederverwendung von Klassen!

Du musst Dir also überlegen, was Du brauchst. Wenn Du ein Lager für Tiere bist, dann nimmst Du ein Tier entgegen. Bist du eine Hundepension, dann nimmst Du nur Hund entgegen.... Willst Du einen Schäferhund haben, dann nimmst Du nur einen Schäferhund entgegen. (Wärst ja auch schön blöd, wenn Du sagst: Ich will ein Schäferhund und dann gibt man dir einen kleinen Dackel..... Das geht einfach nicht.
 
I

iRecordS

@JustNobody Das Thema mit Interfaces habe ich auch noch nicht ganz verstanden.. wieso sollte ich Klassen haben, welche Variablen und Methoden/Funktionen deklarieren aber nicht initialisieren? In welchen Momenten brauch ich das?

Wenn Du ein weitergehendes Verhalten willst, dann musst Du erst prüfen, ob der Typ stimmig ist:
Ist es ein Hund? -> Auf das Verhalten des Hundes zugreifen.
Ist es eine Katze? -> Auf das Verhalten der Katze zugreifen.
Wie überprüfe ich das denn in dem Falle von Tier neuesTier = new Hund(); ? Kann ich einfach eine Abfrage machen mit:
neuesTier == Hund();? Das sieht jetzt schon falsch aus. ^^
 
W

White_Fox

@JustNobody Das Thema mit Interfaces habe ich auch noch nicht ganz verstanden.. wieso sollte ich Klassen haben, welche Variablen und Methoden/Funktionen deklarieren aber nicht initialisieren? In welchen Momenten brauch ich das?
Das brauchst du dann, wenn du erzwingen willst daß ein Objekt ein bestimtmes Verhalten anbietet, aber man nicht wissen kann/will, wie es implementiert wird.

Erstmal ist es wichtig, scharf zu trennen: Ein Interface ist keine Klasse. Sondern es stellt eine Schnittstelle dar, daher in der Regel die Terminologie mit xable. (Serializable, Comparable, ...)

Grob gesagt: Eine Klasse legt fest, was ein Objekt ist. Ist es ein Tier, ein Landfahrzeug, ein Küchengerät, usw. Ein Interface erweitert eine Klasse um eine Fähigkeit, dadurch kann ein beliebiges Objekt schwimmfähig, flugfähig, ... werden.

Beispiel: Du kannst von deiner Klasse "Tier" die Klasse "Säuger" ableiten. Säuger sind z.B. auch Fledermäuse, Flughunde. Diesen beiden Klassen kannst du z.B. ein Interface Flyable mitgeben und dadurch erzwingen, daß diese eine Methode fly(); haben.
Genauso kannst du von "Tier" auch noch "Fisch" ableiten und eine Klasse "FliegenderFisch". Auch dieser Klasse kannst du das Interface "Flyable" mitgeben, aber die Methode muß offensichtlich ganz anders implementiert werden, da ein FliegenderFisch längst nicht so weit fliegt wie eine Fledermaus.
Auf jeden Fall kannst du damit alle Flugfähigen Tiere gleich behandeln, wenn du ein Flyable flieger = new Fledermaus(); instanzierst.


Wie überprüfe ich das denn in dem Falle von Tier neuesTier = new Hund(); ? Kann ich einfach eine Abfrage machen mit:
neuesTier == Hund();? Das sieht jetzt schon falsch aus. ^^
Schau dir mal den instanceOf-Operator an.
 
W

White_Fox

Um mal auf die Frage im Threadtitel zurückzukommen:

Klassen sind NICHT das Äquivalent zu Strukturen in C. Klassen sind keine Datentypen.
Ich muß allerdings zugeben, mehr habe ich in Klassen eigentlich auch nicht gesehen bevor ich mich ernsthaft mit Objektorientierung beschäftigt habe. Ich würde dir raten, mal ein gutes Buch zu Entwurfsmustern zu lesen, das wird dir auf vieles einen ganz neuen Blick geben.

 
I

iRecordS

@White_Fox
Um mal auf die Frage im Threadtitel zurückzukommen:

Klassen sind NICHT das Äquivalent zu Strukturen in C. Klassen sind keine Datentypen.
Mit C kenne ich mich leider gar nicht aus. Allerdings, was ist die Klasse denn sonst in diesem Falle: Tier hund;

Auf jeden Fall kannst du damit alle Flugfähigen Tiere gleich behandeln, wenn du ein Flyable flieger = new Fledermaus(); instanzierst.
Ich kann Interfaces ebenfalls als "Datentyp" nutzen? Und außerdem, wenn ich mir deine Erklärung so durchlese, dann wirkt es für mich sinnvoll, für jede Kindsklasse ein neues Interface (bzw. ein Interface für mehrere Kindsklassen wo es zutrifft, wie hier mit flyable) zu erstellen und die jeweiligen abweichenden Funktionen zu erstellen, welche die Vaterklasse nicht anbietet.
 
W

White_Fox

Allerdings, was ist die Klasse denn sonst in diesem Falle: Tier hund;
Eine Struktur in C ist im Prinzip ein Bündel aus anderen Datentypen. Du kannst z.B. eine Struktur "Rectangle" erstellen, die zwei Doubles enthält und nennst diese a und b.

Das kannst du mit einer Klasse auch machen. Aber, und das ist der Unterschied: Du kannst einer Klasse auch ein Verhalten beibringen. Beispiel:
Java:
public abstract class Tier{
    public abstract void gibLaut();
}

public class Katze extends Tier{
    @Override
    public void gibLaut(){
        System.out.println("Miau Miau");
    }
}

public class Hund extends Tier{
    @Override
    public void gibLaut(){
        System.out.println("Wuff Wuff");
    }
}
Hier passiert folgendes: In der Klasse Tier wird festgelegt, daß es eine Methode gibLaut() geben muß. Die Methode ist abstrakt, d.h. es ist festgelegt daß sie da sein muß, Rückgabetyp, welche Parameter erwartet werden, usw. Damit muß jede Klasse, die von Tier abgeleitet wird, diese Methode implementieren. Das machen die Klassen Hund und Katze, aber wie du siehst muß jede Methode anders implementiert werden, damit es für die jeweilige Klasse paßt.



Ich kann Interfaces ebenfalls als "Datentyp" nutzen? Und außerdem, wenn ich mir deine Erklärung so durchlese, dann wirkt es für mich sinnvoll, für jede Kindsklasse ein neues Interface (bzw. ein Interface für mehrere Kindsklassen wo es zutrifft, wie hier mit flyable) zu erstellen und die jeweiligen abweichenden Funktionen zu erstellen, welche die Vaterklasse nicht anbietet.
NEIN! Ein Datentyp ist etwas um bestimmte Daten zu speichern, das macht ein Interface nicht. Ich meinte mit "gleich behandeln" so etwas:
Java:
public void someMethode(Flyable flyable){
    flyable.fly();  //Völlig egal ob das Flyable eine Fledermaus oder sonstwas ist.
}
 
T

temi

Klassen sind keine Datentypen.
Hm. Dieser Aussage möchte ich nicht zustimmen. Eine Klasse "kann natürlich mehr" als ein primitiver Datentyp, aber es ist nach meinem Dafürhalten dennoch ein Datentyp, nämlich ein Referenztyp.

Aus dem Beispiel Tier hund; ist "hund" die Variable vom Typ "Tier".
 
W

White_Fox

Du kannst eine Klasse natürlich als selbstdefinierten Datentypen verwenden. Aber du kannst einem Datentypen kein weiteres Verhalten beibringen, insofern würde ich bei meiner Aussage bleiben.

Aber das ist Definitionssache, davon hab ich wiederum kaum Ahnung, ich nehme daher einfach mal an daß du Recht haben könntest. :)
 
mrBrown

mrBrown

Erstmal ist es wichtig, scharf zu trennen: Ein Interface ist keine Klasse. Sondern es stellt eine Schnittstelle dar, daher in der Regel die Terminologie mit xable. (Serializable, Comparable, ...)

Grob gesagt: Eine Klasse legt fest, was ein Objekt ist. Ist es ein Tier, ein Landfahrzeug, ein Küchengerät, usw. Ein Interface erweitert eine Klasse um eine Fähigkeit, dadurch kann ein beliebiges Objekt schwimmfähig, flugfähig, ... werden.
Da würde ich zumindest in Teilen widersprechen.

Ein Interface legt genauso wie eine Klasse fest, was ein Objekt ist, in deinem Beispiel könnte Tier und Landfahrzeug auch ein Interface sein (wie zB List im JDK). Die Interfaces stellen nicht eine "Fähigkeit" zur Verfügung, sondern stellen einen ganzen Typen da, Benennung wie bei Klassen daher üblicherweise ein Nomen, die meisten Interfaces dürften dieser Art sein. Für den Nutzer des Types sollte es dabei vollkommen egal sein, ob er Interface oder Klasse nutzt.

Auf *able endende Interfaces stellen entweder, wie du auch beschrieben hast, Typen-übergreifende "Fähigkeiten" dar, oder dienen als Marker-Interface.
Für ersteres ist Comparable ein gutes Beispiel, oder aush Iterable, was zB von List erweitert wird.
Zweitens ist zB Serializeable oder Cloneable - das Interface stellt selber keinerlei Methoden zur Verfügung, sondern dient nur als "Markierung", dass der implementierende Typ irgendeine Besonderheit aufweist, die allerdings keine Methoden sind.
 
Thema: 

Klassen als Datentyp nutzen?

Passende Stellenanzeigen aus deiner Region:
Anzeige

Neue Themen

Anzeige

Anzeige
Oben