Vererbungshierachie und java.util.Date

andrade

Mitglied
Hallo zusammen,
aus einer alten Prüfung beschäftige ich mich gerade mit dieser Aufgabe:
Programmieren Sie in Java eine Vererbungshierarchie mit Klassen und Interfaces für folgende Objekttypen: Fleischfresser, Pflanzenfresser, Tier, Saeugetier, Loewe, Elefant, Schwein, Vogel, Adler, Fisch und Hai. Berücksichtigen Sie folgende Beziehungen:
· Jedes Saeugetier ist ein Tier.
· Jeder Vogel ist ein Tier.
· Jeder Fisch ist ein Tier.
· Jeder Loewe ist ein Saeugetier und ein Fleischfresser.
· Jeder Elefant ist ein Saeugetier und ein Pflanzenfresser.
· Jedes Schwein ist ein Saeugetier und ein Fleischfresser und ein Pflanzenfresser.
· Jeder Adler ist ein Vogel und ein Fleischfresser.
· Jeder Hai ist ein Fisch und ein Fleischfresser.
Das Geburtsdatum eines jeden Tieres soll in einem public-Attribut vom Typ java.util.Date gespeichert werden können (ausnahmsweise nicht private, damit wir uns hier die get- und set-Methoden sparen können). Verzichten Sie auf weitere Attribute. Lassen Sie auch alle Konstruktoren und Methoden weg. Es genügt jeweils der Klassen- bzw. Interface-Rahmen und, wo erforderlich, das Attribut für das Geburtsdatum. Stellen Sie sicher, dass nur von Loewe, Elefant, Schwein, Adler und Hai tatsächlich Objekte bzw. Instanzen erzeugt werden können.


Das ganze sieht dann wie im Bild aus. Also Saeugetier, Vogel, Fisch sind abstrakt, damit es keine Instanzen von Ihnen geben kann. Fleisch- und Pflanzenfresser sind Interfaces (weil?).
Das Geburtsdatum müssen ja nur Löwe, Elefant, Schwein, Adler und Hai haben.
Doch dort häng ich fest, der Import in den besagten Klassen ist klar.
Aktuell sieht es bei mir beispielsweise so aus:
Java:
import java.util.Date;
public class Hai extends Fisch implements Fleischfresser
{  
    public Hai()
    {
      Date date = new Date();
    }  
}

Ist das schon alles?
 
Ich kann dein Bild hier in der Firma leider nicht öffnen, aber der gepostete Code ist nicht ganz richtig. Du erstellst im Konstruktor nur eine lokale Variable, weist den Wert aber nicht der Klassen-Variable zu. Außerdem wäre es sinnvoller diese Zuweisung im Konstruktor der Klasse Tier durchzuführen, dann musst du den Code nur an einer einzigen Stelle schreiben. Die Klasse Tier ist die Stelle, an der du das Geburtsdatum speichern solltest! Es ist zudem ja auch explizit gesagt, dass du gar keine Konstruktoren schreiben sollst.
Die Klassendefinition "public class Hai extends Fisch implements Fleischfresser" ist aber richtig.
 
Jeder Tier hat ja ein Geburtsdatum, daher ist das eine Instanzvariable vom Tier. Und wenn das im Konstruktor gesetzt werden soll, dann kann das der Konstruktor vom Tier machen. Oder man macht es direkt bei der Definition der Instanzvariable:
Code:
import java.util.Date;
public class Tier
{
   protected Date geburtstag = new Date();

  public Date getGeburtstag() {
    return geburtstag;
  }

  public void setGeburtstag(Date geburtstag) {
    this,geburtstag = geburtstag;
}

Das nur einmal direkt hier im Editor geschrieben und kann daher Tippfehler enthalten. JavaDoc Kommentare fehlen noch. Ob die Instanzvariable protected oder private ist könnte man sich jetzt streiten. Und natürlich die üblichen Setter/Getter für so eine Instanzvariable.

Konrad
 
Da haben sowohl ich als auch VfL_Freak nicht gut genug gelesen:

"Das Geburtsdatum eines jeden Tieres soll in einem public-Attribut vom Typ java.util.Date gespeichert werden können (ausnahmsweise nicht private, damit wir uns hier die get- und set-Methoden sparen können)."

Also public und getter/setter weglassen. Und java.util.Date ist explizit gefordert.

Aber danke VfL_Freak: joda-time kannte ich bisher nicht, wobei ich jetzt auch nur mit jdk8 arbeite, so dass java.time zu nutzen wäre.
 
Danke euch beiden! Kommentare müssen wir in der Klausur nicht machen, ist zeitlich wohl auch gar nicht machbar.
Wenn ich noch einmal über die Aufgabe lese, find ich Sie auch etwas unklar.
Soll das Geburtsdatum per set Methode eingebeben werden oder quasi automatisch bei der Erzeugung der Tiere gesetzt werden? Wahrscheinlich wird es auf obige Variante hinauslaufen, aber 100% klar wird das meiner Meinung nach nicht.
Habt ihr zufällig noch Tipps, wie ich mir merken kann, wann ich ein Interface benutze?
Immer dann, wenn es Mehrfachvererbung geben kann? Also hier z.B. ist das Schwein Fleisch und pflanzenfresser.
 
Moin,

Da haben sowohl ich als auch VfL_Freak nicht gut genug gelesen:
"Das Geburtsdatum eines jeden Tieres soll in einem public-Attribut vom Typ java.util.Date gespeichert werden können (ausnahmsweise nicht private, damit wir uns hier die get- und set-Methoden sparen können)."
Also public und getter/setter weglassen. Und java.util.Date ist explizit gefordert.
Oops, you are absoluty right 😎

Aber danke VfL_Freak: joda-time kannte ich bisher nicht, wobei ich jetzt auch nur mit jdk8 arbeite, so dass java.time zu nutzen wäre.
Stimmt, mit Java8 soll das besser geworden sein!
Ich war jetzt nur vom verwendeten Typ "java.util.Date" aus gegangen, der ja nun deutlich älter und eben mieser ist !

Gruß Klaus
 
Habt ihr zufällig noch Tipps, wie ich mir merken kann, wann ich ein Interface benutze?
Immer dann, wenn es Mehrfachvererbung geben kann? Also hier z.B. ist das Schwein Fleisch und pflanzenfresser.
Immer wenn du beschreiben willst das mehrere Objekte eine bestimmte Fähigkeiten oder Eigentschaften haben sollen.

Wenn zum Beispiel ein Onlineshop Pilze anbietet und dieser seinen Kunden mitteilen möchte ob diese auch Essbar sind, kann man das Interface Essbar erstellen. Das sagt uns das ein Pilz die Fähigkeit oder Eigenschaft besitzt gegessen zu werden... (klingt komisch^^)

Jetzt haben wir weiterhin ein Objekt Steinpilz, Champion und Fliegenpilz. Diese könnten wir zu dem abstrakten Konstrukt Pilz zusammenfassen.
Java:
public abstract class Pilz{
}
Java:
public class Steinpilz extends Pilz{
}
Java:
public class Fliegenpilz extends Pilz{
}

Bisher wissen wir nur das unser Steinpilz auf jeden Fall auch vom Typ Pilz ist. Wir haben keine Ahnung ob wir den nun auch essen dürfen. Gleiches gilt für den Fliegenpilz.

Durch das Interface Essbar können wir nun bestimmen welcher der Pilze essbar ist und welcher eben nicht.

Java:
public class Steinpilz extends Pilz implements Essbar{
}

Jetzt ist unser Steinpilz essbar. Der Fliegenpilz allerdings nicht, da er nicht das Interface Essbar implementiert.

Das Interface könnte man nun noch erweitern und einen getter wie zum Beispiel Geschmack hinzufügen.

Java:
public interface Essbar{
String getGeschmack();
}

Somit muss unser Steinpilz auch angeben wie er schmeckt.

Java:
public class Steinpilz extends Pilz implements Essbar{
   @Override
   public String getGeschmack() {
      return "schleimig";
   }
}

Dadurch wissen wir auch, dass wir jeden beliebigen Essbaren Pilz fragen könnten wie er schmeckt. (Also mehrere Objekte haben die gleiche Eigenschaft, deswegen können wir sie auch gleich behandeln)

Achja um nochmal auf den Shop zurückzukommen... man könnte alle essbaren Pilze in eine Liste speichern und der Kunde kann dann das ganze Essbare Zeug ausgeben lassen. (Hier nur den Geschmack))
Java:
//...
List<Essbar> essbaresZeug = new ArrayList<Essbar>();

public void zeigeMirDenGeschmackVonAllenEssbarenZeugs() {
        for(Essbar essbar : essbaresZeug) {
            System.out.println(essbar.getGeschmack());
        }
}
//...
 
Zuletzt bearbeitet:
Danke Jardcore! Das hilft mir weiter.

Es gibt nun noch eine Erweiterungsaufgabe zu obiger:
Programmieren Sie eine vollständige Java-Klasse namens Zoo mit folgenden Eigenschaften/Operationen:
· Ein Zoo beherbergt eine beliebige Anzahl von Tieren aller Art. (Die Klassen für die Tiere stammen aus Aufgabe 1. Nehmen Sie an, diese seien komplett fertiggestellt und könnten hier ohne Änderung verwendet werden.)
· Eine Methode, mit der ein neues Tier in den Zoo aufgenommen werden kann. Bei der Aufnahme wird jedem Tier ein eindeutiger, persönlicher Namen gegeben (Beachte: Name ist kein Bestandteil der Tier-Klassen aus Aufgabe oben).
· Eine Methode, mit der ein Tier unter Angabe seines persönlichen Namens wiedergefunden bzw. abgefragt werden kann.
Hinweis: Wenn Sie keine Lösung für die Zuordnung der Namen wissen, können Sie auf die Namen verzichten und ersatzweise zur Abfrage eines Tieres dessen fortlaufende Nummer (Index) verwenden. In diesem Fall sind jedoch maximal 8 Punkte erreichbar.

Hier muss ich sagen, dass ich da total auf dem Schlauch stehe und ich bereits am Anfang scheitere. Könnt ihr mir auf die Sprünge helfen?
 
Also was Du machen sollst ist einfach nur das Speichern von Werten (hier Tiere) mit einem Key (Hier Name). Das sind dann typischerweise Maps. Du könntest z.B. eine HashMap verwenden, die als Key einen String nimmt für den Namen und dann das Tier als Wert.
 
gut, dass die HashMap hier gefordert ist. Die hatte ich mir bisher vergessen anzusehen 🙂
Hier hab ich aber noch meine Schwierigkeiten.
Java:
import java.util.HashMap;
public class Zoo
{
    public Zoo (){
        HashMap<String, Tier> tiere = new HashMap<String, Tier>();
        tiere.put("Heinz",new Elefant());
    }   
    public void tierHinzufuegen(String s, Tier t)
    {
        tiere.put(s,new AUFRUF);
    }
}
Die HashMap an sich,sollte ja so funktionieren. Über tiere.put("Name", New Tierart()); kann ich im Konstruktor auch direkt welche hinzufügen, aber wie mach ich das mit einer Methode? Ich muss ja irgendwie die Art des Tieres für den new Operator übergeben.
 
Moin,
verstehe Deine Frage nicht so ganz ...

Suchst Du sowas ?? 😵
Java:
import java.util.HashMap;
public class Zoo
{
    public Zoo ( Elefant myElefant )
    {
        HashMap<String, Tier> tiere = new HashMap<String, Tier>();
        tiere.put("Heinz", myElefant);
    } 
    public void tierHinzufuegen(String s, Tier t)
    {
        tiere.put(s, t);
    }
}

Gruß Klaus
 
Leute aufpassen! Im Konstruktor habt Ihr jetzt eine lokale Variable tiere definiert, die natürlich mit Ende des Konstruktors auch weg ist. Also bitte die HashMap als Instanzvariable definieren.

Und wie VfL_Freak richtig gezeigt hat: Du braucht gar keinen new Aufruf. Du bekommst ja das Tier, das gespeichert werden soll, als Parameter und speicherst das einfach.

Das Beispiel im Konstruktor erstellt ja einfach nur den Elefanten. Das könnte also dann auch so geschrieben werden:
Code:
import java.util.HashMap;
public class Zoo
{
   private HashMap<String, Tier> tiere;

   public Zoo ( Elefant myElefant )
   {
        tiere =new HashMap<String, Tier>();
        tierHinzufuegen("Heinz", new Elefant());
   }
   public void tierHinzufuegen(String s, Tier t)
   {
        tiere.put(s, t);
   }
}
Das new ist also nur verwendet worden, um eine Instanz zu erzeugen. Aber wir wollen ja beim speichern keine neue Instanz erzeugen sondern das übergebene Tier hinzu fügen.

Konrad
 
Ich möchte ja ein Tier über die Methode tierHinzufügen (z.B. Elefant) kreieren. Dazu muss ich den String (namen) übergeben und dann ja über den new Elefant das Tier erzeugen. die Frage ist nun, wie ich das ganze mache.
 
Die Funktion heisst doch tierHinzufügen und nicht tierErzeugen. Also willst Du doch ein Tier nur hinzufügen und nicht ein Tie erzeugen. Also nichts mit new Operator. Das kommt nur an anderen Stellen - wo Du halt wirklich ein Tier hinzufügen möchtest.
 
Ach mensch. Total blöd von mir. Den Fehler seh ich ein.
Aber wenn ich nun die Klasse Zoo habe und folgendes in die Konsole eingebe:
Zoo zoo1 = new Zoo();
Hai hai1 = new Hai();
zoo1.tierHinzufuegen("kai", hai1)
Exception occurred.
bekomme ich einen Fehler. mit NullPointerException in der Zeile tiere.put(s,t);
 
Hast du einen Konstruktor in Zoo, in dem Du tiere = new HashMap..... stehen hast?
Ganz wichtig: Ohne HashMap vor tiere!
 
Ich hab dann nun
Java:
import java.util.HashMap;

public class Zoo
{
    private HashMap<String, Tier> tiere;
    public Zoo (){
        tiere = new HashMap<String, Tier>();
    }

    public void tierHinzufuegen(String s, Tier t)
    {
        tiere.put(s,t);
    }

    public void findeTier (String s)
    {                  
            System.out.print("Key: " + s + " - ");
            System.out.print("Value: " + tiere.get(s) + "\n");
    }   
}
das scheint so weit zu funktionieren. gibt es Kritik?
jetzt müsste ich nur noch schauen, dass der Name eindeutig ist und vlt die Ausgabe netter gestalten.
 
Die HashMap speichert nur eindeutige Keys. Also wenn Du etwas hast wie:
Code:
zoo1.tierHinzufuegen("Otto", new Elephant());
zoo1.tierHinzufuegen("Otto", new Affe());
dann wird durch den zweiten Aufruf der Elephant Otto durch den Affen Otto ersetzt.
 
wieder etwas gelernt! Danke! Dann müsste die Aufgabe ja so gelöst sein.
oder kann ich die Ausgabe mittels return statt system.out machen?
 
es heißt ja, dass das tier unter angabe des Namens wiedergefunden bzw abgefragt werden kann.
Da frag ich mich, ob es reicht, wenn das per System.out ausgebe oder per return zurückgebe
 
Moin,

meinst Du Deine Methode "findeTier" ??
Die läßt derzeit keine Rückgabewerte zu ... Du müsstest dann erstmal das "void" in den gewünschten Rückgabetyp ändern !!

Gruß Klaus
 
Also findeTier sollte das Tier schon zurück geben. Du weisst ja nicht, wozu das Tier herausgesucht wurde.
 
Um ein Tier zurückzubekommen müsste der Getter so aussehen:
Java:
public Tier getTier(String name) {
    return tiere.get(name);
}
Du könntest dein Tier Objekt noch erweitern und ihm einen Namen verpassen. Dann hättest du ein paar elegantere Aufrufe.
Java:
public class Tier {
    private String name;
    public Tier(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @Overide
    public String toString() {
        return "Ich bin " + name + " der " + getClass().getSimpleName();
    }
}
In deinem Zoo könntest du dann die Methode "tierHinzufuegen()" wie folgt abändern.

Java:
public void tierHinzufuegen(Tier tier) {
    tiere.put(tier.getName(), tier);
}

Und so würdest du es Aufrufen:
zoo.tierHinzufügen(new Elephant("Otto"));

Den Getter von Oben kann man zum Beispiel so verwenden.

System.Out.println(zoo.getTier("Otto"));

Die Ausgabe würde so aussehen:
"Ich bin Otto der Elefant"

Sieht jetzt bisschen komisch aus, das liegt daran das wir oben der Tier Klasse eine toString Methode verpasst haben. Diese sorgt dafür das wenn wir unser Objekt ausgeben wollen, diese Methode explizit aufgerufen wird.
 
Nur eben lässt dies die eigentliche Aufgabe nicht zu:
"Verzichten Sie auf weitere Attribute. Lassen Sie auch alle Konstruktoren und Methoden weg."

Aber eine interessante Übung wäre sowas schon. Tier selbst muss ansonsten noch abstrakt sein, da sonst eine Instanz von Tier angelegt werden kann.
 
Ich hab jetzt noch eine kleine Anpassung vorgenommen, so dass das Tier zurückgegeben wird.
Java:
public String findeTier (String s)
    {                  
            return tiere.get(s).toString();
    }

Wobei ich in dieser und auch obiger Variante mit system.out als Ausgabe z.B. "Löwe@1ca4e8ea" also Tier und Speicheradresse. Weiß jemand, wie ich letzteres wegbekommen kann?
 

Zurück
Oben