Objektorientierte Beziehungsfragen

Assoziationen

Ein Fernseher, eine Bildröhre und eine 1:1 Assoziationen mit Delegation (30 Minuten)

Implementiere eine Klasse TV mit dem Attribut int program, zudem Methoden on()/off(), die kurze Meldungen auf die Konsole schreiben (ein Attribut ist für das Beispiel erst einmal nicht nötig). Die Klasse soll in einer eigenen Datei (Compilationseinheit) definiert werden. Genutzt werden soll das TV über die main(...)-Methode im HousingComplex.

Ein TV soll eine MonitorTube (Bildröhre) über ein privates Attribut referenzieren. Wie kann das in Java Quellcode aussehen? Implementiere eine unidirektionale Beziehung zwischen dem Fernseher und der Bildröhre.

Die MonitorTube soll ebenfalls on()/off()-Methoden mit Konsolenmeldungen bekommen.

Wenn das TV ein-/ausgeschaltet wird, so soll auch die Bildröhre ein-/ausgeschaltet werden.

Überlege, wie eine bidirektionale Beziehung implementiert werden kann. Wo könnte ein Problem lauern?

Lösung

1:n Assoziationen *

Lege eine neue Klasse House (ohne main(...)-Methode) an. Der HousingComplex soll in main(...) zwei Häuser aufbauen.

Damit sich das House mehrere Radios merken kann, müssen wir eine Datenstruktur (wie java.util.ArrayList) verwenden:

 ArrayList<Radio> radios = new ArrayList<>();

Weise einem House in der main(...)-Methode im HousingComplex mehrere Radios zu. Wie kommen Radios in das Haus?

Schreibe eine House-Methode int numberOfActiveRadios(), die liefert, wie viele Radios an sind.

Optional: Gib dem Haus ebenfalls eine toString()-Methode.

Was muss man tun, wenn das Haus auch andere Elektrogeräte speichern möchte, etwa DVD-Spieler oder Fernseher?

Lösung

Vererbung

Ein Radio ist ein Elektrogerät und viele andere Geräte auch

Lösung

Konstruktor-Aufrufe in der Kette

Setze in den Konstruktor von ElectricAppliance und Radio in die erste Zeile eine Konsolenausgabe. Beobachte die Reihenfolge.

private und protected Konstruktor

Schreibe eine leere Klasse mit einem privaten Konstruktor. Welche Fehlermeldung folgt, wenn ein Objekt der Klasse gebildet werden soll. Bilde eine Unterklasse. Was ist hier die Meldung des Compilers? Ändere das Zugriffsrecht auf protected. Wer kann jetzt ein Exemplar der Klasse bilden?

Vererbungsfragen *

Werden Konstruktoren vererbt? Erkläre das Verhalten der beiden Klassen.

class A {
 int number;
 A( int i ) {
   number = i;
 }
}
class B extends A {
  static public void main( String args[] ) {
    B b1 = new B();
    B b2 = new B(3);
  }
}

Was sollte man daher in Klassen beachten, von denen andere Klassen abstammen sollen und die die Konstruktoren mit Argumenten implementieren?

 Varargs mit Elektrogeräten

Setze in der Klasse ElectricAppliance eine statische Methode:

public static int numberOfActiveAppliances( ElectricAppliance... appliances ) {
  // Liefert die Anzahl eingeschalteter Geräte zurückgeben,
  // die der Methode übergeben wurden
}

Wenn etwa r1 und r2 zwei eingeschaltete Radios sind, und dvd ein ausgeschalteter DVD-Spieler, ist

int i = ElectricAppliance.numberOfActiveAppliances( r1, dvd, r2 ); // i = 2

Lösung

Ein Haus als Container

Das Haus hat vorher nur konkrete Typen wie Radios gespeichert. Nun sollen allgemeine Elektrogeräte gespeichert werden. Der Typ der dynamischen Datenstruktur ändert sich also in:

private ArrayList<ElectricAppliance> appliances = new ArrayList<>();

Wie ändern sich die Typen in der Hinzufügemethode?

Wenn in der Hinzufügemethode ein Radio aufgenommen wird, soll folgende Konsolenausgabe erfolgen: "Radio wurde hinzugefügt, schon GEZahlt?".

Überschreiben von Methoden (10 Minuten)

Implementiere eine Klasse Firebox für einen Feuermelder als Unterklasse von ElectricAppliance.

Die Methode off() soll mit leerem Rumpf implementiert werden, sodass sich der Feuermelder nicht ausschalten lässt.

Aufruf der Methoden der Oberklasse

Die TV-Klasse hat schon Methoden on()/off(). Erweitert auch TV die Klasse ElectricAppliance überschreibt ein Fernsehen somit die Methoden der Oberklasse ElectricAppliance. Es ergibt sich aber ein Problem:

Wie lässt sich das Problem lösen?

Polymorphie und dynamisches Binden

Polymorphie is in the House

Implementiere in der House-Klasse eine Methode holiday(), die alle Elektrogeräte der Liste ausschaltet.

public void holiday() {
  // rufe off() für alle Elemente in der Datenstruktur auf
}

In der main(...)-Methode vom HousingComplex steht dann:

Radio schlafzimmerradio = new Radio();
Radio wohnzimmerradio = new Radio();
TV bügeltv = new TV();
Radio kloRadio = new Radio();
Firebox alarm = new Firebox();
House h = new House(); 
h.addAppliance( schlafzimmerradio );
h.addAppliance( wohnzimmerradio );
h.addAppliance( bügeltv );
h.addAppliance( kloRadio );
h.addAppliance( alarm );
h.holiday();

Lösung für die Klasse House

Abstrakte Klassen

Abstrakte Klasse

Kann man Exemplare von ElectricAppliance anlegen? Muss man das können?

Abstrakte Klasse realisieren

Ein java.util.Timer kann Aufgaben wiederholt durchfüren. Dazu hat die Timer-Klasse eine Methode schedule(...) zum Hinzufügen einer Aufgabe. Die Aufgabe ist vom Typ java.util.TimerTask. Prüfe in der API-Dokumentation, ob die Klasse abstrakt ist und ob Methoden abstrakt sind.

Schreibe eine Unterklasse von TimerTask, die immer dann eine Meldung auf dem Bildschirm ausgibt, wenn die Anzahl freier Bytes auf dem Dateisystem unter eine gewisse Grenze fällt (z.B. weniger als 1000 MiB). Die freien Bytes liefert:

long freeDiskSpace = new File( "C:" ).getFreeSpace();

Der Timer soll diesen TimerTask alle 2 Sekunden ausführen.

Lösung

Schnittstellen

Schnittstelle Comparator implementieren

Ein Elektrogerät hat einen Verbrauch (Watt). Deklariere ein privates double-Attribut watt und Setter/Getter. Eine toString()-Methode soll etwa folgendes zurückgeben: "ElectricAppliance[watt=12kW]". Bedenke, dass die Unterklassen toString() überschreiben!

Schreibe eine neue Klasse ElectricApplianceWattComparator, die die Schnittstelle java.util.Comparator<ElectricAppliance> implementiert. Die compare(...)-Methode soll eine Ordnung der Elektrogeräte definieren, wobei ein Elektrogerät "kleiner" ist, wenn es weniger verbraucht.

Comparator<ElectricAppliance> c = new ElectricApplianceWattComparator();
System.out.println( c.compare(eg1, eg2) );
System.out.println( c.compare(eg2, eg3) );
System.out.println( c.compare(eg1, eg3) );

Setzte zum besseren Verständnis ein println(...) in die eigene compare(...)-Methode, sodass man sehen kann, welche Objekte verglichen werden.

Lösung

Schnittstelle Comparator für größtes/kleinstes Element einsetzen

Die Klasse java.util.Collections hat eine statische Methode max(...), die das größte Elemtent einer Sammlung liefert. Übergeben werden muss der Methode a) eine Collection (wie ArrayList) und b) ein Comparator. Hier lässt sich unser ElectricApplianceWattComparator verwenden.

Setze in das Haus eine Methode findMostPowerConsumingAppliance(), die das Gerät mit dem größten Verbrauch liefert.

Schnittstelle Comparator zum Sortieren einsetzen

Möchte man String-Objekte eines Arrays sortieren, kann man die Arrays.sort(...)-Methode nutzen. Möchte man eigene Objekte in eine Reihenfolge bringen, so muss man der sort(...)-Methode sagen, wann ein Objekt etwa "kleiner" ist als ein anderes. Dazu lässt sich unser ElectricApplianceWattComparator verwenden; er ist Voraussetzung für Objekte, die man sortieren möchte -- das verrät auch schon die Signatur sort(Object[] a, Comparator<...> c).

ElectricAppliance[] array = { eg1, eg2, eg3 };
System.out.println( Arrays.toString(array) );
Arrays.sort( array, new ElectricApplianceWattComparator() );
System.out.println( Arrays.toString(array) );

Stehen String-Objekte in einer ArrayList, kann man Collections.sort(...) nutzen oder auch die sort()-Methode direkt auf auf der Liste.

Kompakte Implementierungen

Gib jedem Elektrogerät auch ein Gewicht. Schreibe einen zweiten Comparator, der nach Gewicht sortiert. Schreibe den Comparator so kompakt wie möglich.