pp.04.01-RunnableReturn

Thread für Runnable mit Rückgabewert

  • Projekt: pp.04.01-RunnableReturn
  • Bearbeitungszeit: 30 Minuten
  • Musterlösung: 15 Minuten
  • Kompatibilität: mindestens Java SE 10

Es gibt viele Aufgaben, die man asynchron zum restlichen Programmfluss erledigen kann, die aber gleichzeitig ein konkretes Ergebnis zurückgeben sollen (z.B. Berechnungen, holen einer Information aus einem Backend über einen Netzwerkaufruf, …).

Für diese Situation soll in dieser Aufgabe eine wiederverwendbare Klasse programmiert werden.

Quellcode des Interface Expression

@FunctionalInterface
public interface Expression<T> {
    public T eval();
}

Das Interface Expression<T> repräsentiert eine Aufgabe mit Rückgabewert vom Typ T. Expression<T> ist ein @FunctionaInterface: Es besitzt genau eine Methode, so dass es für Lambda-Ausdrücke benutzt werden kann. Die eigentliche Berechnung wird ausgeführt, wenn die zu implementierende Methode eval() ausgeführt wird. Der Rückgabewert von eval() ist das Ergebnis der Berechnung.

Quellcode von RunnableWithResult

In der Klasse RunnableWithResult<T>, die das Interface Runnable implementiert, soll solch eine Aufgabe repräsentiert, ausgeführt und abgerufen werden können. Der Konstruktor erwartet ein Objekt, das Expression<T> implementiert. Es muss also die Methode eval() bereitstellen.

public class RunnableWithResult<T> implements Runnable {
    private final Expression<T> expr;

    public RunnableWithResult(Expression<T> expr) {
        this.expr = expr;
    }

    @Override
    public void run() {
        // hier programmieren
    }

    public synchronized Boolean isAvailable() {
        // hier programmieren
        return null;
    }

    public synchronized T get() {
        // hier programmieren
        return null;
    }

    public Expression<T> expr() {
        return expr;
    }
}

Aufgaben

Die Methode run() in RunnableWithResult muss überschrieben werden. In der Methode muss der Ausdruck ausgewertet werden. Das Ergebnis sollte in einer Instanzvariablen gespeichert werden. Um den Inhalt der Instanzvariablen aus einem aufrufenden Thread auslesen zu können, wird ein Getter benötigt.

Sollte die Auswertung des Ausdrucks länger benötigen, muss der Aufrufer feststellen können, ob das Ergebnis schon vorliegt. Dafür ist die Methode isAvailable() vorgesehen. Getter und isAvailable() sollen beide asynchron arbeiten: Egal, ob ein Wert vorliegt oder nicht, der Aufruf blockiert nicht.

Programmieren Sie in der vorbereiteten Klasse Main in der Methode main einen beispeilhaften Aufruf: Erzeugen Sie dazu drei RunnableWithResult-Objekte für die Berechnung des Ausdrucks \(((1+2)+(3+4))\), die nebenläufig zum Main-Thread ausgeführt werden. Das Ergebnis soll ausgegeben werden. Sie können Lambda-Ausdrücke oder anonyme innere Klassen benutzen um die Expression’s zu erzeugen.

Achtung: Der Umgang mit Exceptions bei RunnableWithResult<T>, die während der Auswertung des Ausdrucks auftreten könnten, ist in dieser Umsetzung nicht sehr elaboriert. Probieren Sie es aus, indem Sie einen “Division durch 0 Error” in einem RunnableWithResult<>-Ausdruck provozieren. Wann wird die Exception realisiert?