03 Referenzen

Referenzen #

Variablen können primitive Datentypen enthalten. Das sind z.B. die Typen int, double oder boolean, die entsprechenden Werte werden direkt in der Variable gespeichert. Bei der Deklaration als Instanzvariable werden solche Variablen in Java mit Default-Werten (0, 0.0, false) belegt.

Daneben können Variablen aber auch Zeiger (auch Pointer, oder Referenzen) enthalten. In diesem Fall zeigen die Variablen auf Objekte, die dadurch ansprechbar werden. Bei der Deklaration solcher Variablen werden diese zunächst mit dem Wert null belegt.

Wie das genau funktioniert, lernst du in diesem interaktiven Kapitel zu Referenzen in meinem tigyog-Kurs.

Übung für deinen Gelingensnachweis #

Auf einem Handy-Sperrbildschirm sollen Benachrichtigungen angezeigt werden. Folgende Klassen sind dazu bereits entwickelt worden:

Die Klasse Benachrichtigung #

Klassendiagramm zur Klasse Benachrichtigung

Die Klasse Sperrbildschirm #

Klassendiagramm zur Klasse Benachrichtigung

Verwendung #

Betrachte den folgenden Code-Abschnitt:

Sperrbildschirm b = new Sperrbildschirm();
Benachrichtigung n = new Benachrichtigung("ByCS-Messenger", "Hi! Was warn nochmal die HA?");
b.setBenachrichtigung(n);
b.aktualisiereSperrbildschirm();

…und dessen Ergebnis:


---------------------------------

            11:06

 Nachricht von ByCS-Messenger:
 Hi! Was warn nochmal die HA?


---------------------------------

Aufgaben #

  1. Nach weglassen der Zeile b.setBenachrichtigung(n); erscheint die Fehlermeldung
    Fehler: Aufruf der Methode getVonApp des null-Objekts
    
    Erkläre die Fehlermeldung genau und unter Verwendung von Fachbegriffen.
  2. Als Problem wurden die folgenden beiden Zeilen aus der Methode aktualisiereSperrbildschirm identifiziert:
    String nachrichtKurz = " Nachricht von " + this.benachrichtigung.getVonApp() + ":";
    nachrichtKurz += "\n " + this.benachrichtigung.getInhalt();
    
    Ergänze die Stelle so, dass das Problem behoben wird.
  3. Die Methode setBenachrichtigung in der Klasse Sperrbildschirm sieht im Augenblick wie folgt aus:
    public void setBenachrichtigung(Benachrichtigung b) {
       this.benachrichtigung = b;
    }
    
    Sie soll nun so umgeschrieben werden, dass eine neue Benachrichtigung erst angenommen wird, wenn die aktuelle Benachrichtigung als gelesen markiert wurde (d.h. der Aufruf der Methode istGelesen true zurückgibt).
    a) Schreibe die Methode mit den notwendigen Änderungen auf.
    b) Diskutiere die Frage, ob die Benennung und der Typ void bei einem solchen Verhalten der Methode noch angemessen ist. Schlage ggf. einen alternativen Namen und Typ vor.
Lösungsvorschlag
  1. Offensichtlich ist hier versucht worden, an einem null-Objekt eine Methode aufzurufen. Das geschieht insbesondere dann, wenn eine Variable mit einem bestimmten Typ zwar bereits deklariert worden ist, der Variable aber noch keine Referenz auf ein entsprechendes Objekt zugewiesen wurde und dann trotzdem ein Methodenaufruf versucht wird. Weil das Objekt in einem solchen Fall nicht verfügbar ist (weil nichtexistent), können auch keine Methoden daran aufgerufen werden, und das Programm bricht mit einem Fehler ab (mit einer Null-Pointer-Exception).
    (Fachbegriffe erscheinen kursiv)
  2. Eine mögliche Verbesserung:
    String nachrichtKurz;
    if(this.benachrichtigung != null) {
       String nachrichtKurz = " Nachricht von " + this.benachrichtigung.getVonApp() + ":";
       nachrichtKurz += "\n " + this.benachrichtigung.getInhalt();
    } else {
       nachrichtKurz = "";
    }
    
    Erklärung:
    Mit this.benachrichtigung != null wird getestet, ob der Instanzvariable benachrichtigung bereits ein entsprechendes Objekt zugewiesen wurde. Ist dies nicht der Fall, wird der lokalen Variable nachrichtKurz ein leerer String zugewiesen. (Da die Variable offensichtlich später verwendet wird, sollte sie sinnvoll vorhanden sein.)
  3. a)
    public void setBenachrichtigung(Benachrichtigung b) {
       if(this.benachrichtigung == null) {
          this.benachrichtigung = b;
       } else if(this.benachrichtigung.istGelesen()) {
          this.benachrichtigung = b;
       }
    }
    
    Erklärung:
    Das gewünschte Verhalten wird mit this.benachrichtigung.istGelesen() überprüft. Zuvor muss allerdings in irgendeiner Form überprüft werden, ob überhaupt eine Referenz auf ein Benachrichtigungs-Objekt vorliegt – dies geschieht indirekt in der Zeile if(this.benachrichtigung == null) { – wenn dieser Vergleich nicht anschlägt, kann vom Vorhandensein eines entsprechenden Objektes ausgegangen werden.
    b)
    Im Prinzip kann der Typ void beibehalten werden, für eine Setter-Methode wäre das typisch. In diesem Fall spricht allerdings dagegen, dass die Zuweisung einer neuen Benachrichtigung nicht immer erfolgt, und das könnte in anderen Teilen des Programms Probleme verursachen. (So müsste z.B. überprüft werden, ob die Zuweisung geklappt hat.)
    Diese Überprüfung lässt sich vermeiden, wenn ein entsprechender Rückgabewert anzeigt, was der Fall ist. Naheliegend ist true bei Erfolg, und false bei Mißerfolg. (Damit hätte die Methode den Rückgabetyp boolean.)
    Auch die Benennung wäre dann anzupassen, etwa in setBenachrichtigungFallsAlteGelesen – so würde das Verhalten der Methode klarer vorhersehbar sein.