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 MB). Die freien Bytes liefert:

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

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

Bonus: Integriere eine Nachricht in der Tray mit:

try {
  ImageIcon icon = new ImageIcon( new URL( "https://cdn4.iconfinder.com/data/icons/common-toolbar/36/Save-16.png" ) );
  TrayIcon trayIcon = new TrayIcon( icon.getImage() );
  SystemTray.getSystemTray().add( trayIcon );

  trayIcon.displayMessage( "Achtung", "Platte voll", MessageType.INFO );
}
catch ( Exception e ) { e.printStackTrace(); }

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.

ElectricAppliance ea1 = new Radio(); ea1.setWatt( 200 );
ElectricAppliance ea2 = new Radio(); ea2.setWatt( 20 );
Comparator<ElectricAppliance> c = new ElectricApplianceWattComparator();
System.out.println( c.compare(ea1, ea2) );
System.out.println( c.compare(ea2, ea1) );

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.

Lösung

Schnittstelle Comparator zum Sortieren einsetzen

Möchte man Objekte einer Liste sortieren, kann man die Collections.sort(...)-Methode nutzen. Wichtig ist, der sort(...)-Methode mitzuteilen, 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(List<...>, Comparator<...> c).

Nutze in der add(...)-Methode vom House nach dem Hinzufügen in die eigene Datenstruktur Collections.sort(...), um nach dem Hinzufügen immer eine interne sortierte Liste zu haben. Alternativ kann man auch direkt auf der Liste die sort(...)-Methode aufrufen.

Welche Geschäfte sind in der Nähe?

Lege eine neue Klasse Store an mit Point location und String name. Sammle diverse Store-Objekte in einer Liste. Schreibe eine Methode  List<Store> findStoresAround( List<Store> stores, Point center ) { ... }, die eine Liste zurückliefert, nach Abständen zum center sortiert; vorne in der Liste stehen die nächsten.

Lösung

Paket java.util.function, kompakte Implementierungen über Lambda-Ausdrücke

Die List-Methode removeIf(Predicate<...> filter) löscht alle Elemente, die ein Prädikat erfüllen. Möchte man z.B.aus einer Sting<List> aller leeren Strings löschen, kann man removeIf(new IsStringEmpty()); aufrufen, wobei IsStringEmpty so deklariert ist:

class IsStringEmpty implements Predicate<String> {
  @Override public boolean test( String t ) {
    return t == null || t.trim().isEmpty();
  }
}

Setze in das Haus eine neue Methode removePowerConsumingAppliances(), die alle Geräte mit einem Stromverbrauch größer einer selbstgewählten Konstanten MAX löscht.

Schreibe eine neue Methode removeMostPowerConsumingAppliance(), die nur das eine Gerät mit dem höchsten Verbrauch entfernt. Wir nehmen an, dass alle Geräte eine unterschiedliche Watt-Zahl haben.