Bessere Builder in Java mit Manifold: Self Type

In der Vergangenheit hatten wir bereits über die Grundkonzepte des Frameworks Manifold bereichtet, etwa wie man einfache JSON-Schnittstellen aus dem JSON-Objekt erstellen kann, wie man fremde Java-Klassen um Methoden erweitern oder sie sogar von einem Interface erben lassen kann, oder wie man typsichere Reflections realisieren kann. Als weiteres hier besprochenes Feature von Manifold soll es nun um den Self Type gehen und wie dieser dabei helfen kann, das Builder-Pattern besser umzusetzen.

Der Self Type eines Objektes ist, wie aus dem Namen bereits hervorgeht, definiert als der Typ des Objektes selbst. In Java beispielsweise der Typ von this. Diesen Typ hätte man manchmal ganz gerne als Methodenparameter. Zur Veranschaulichung erweitern wir das Customer-Beispiel aus den vorherigen Artikeln um eine Klasse ImprovedCustomer, die von Customer erbt. Außerdem fügen wir zwei Builder für die Klassen Customer und ImprovedCustomer hinzu. Der Code der neuen ImprovedCustomer-Klasse:

public class ImprovedCustomer extends Customer {
    private final String name, email;
    public ImprovedCustomer(long id, String name, String email){
        super();
        super.setId(id);
        this.name = name;
        this.email = email;
    }
}

Der CustomerBuilder ohne build()-Methode zum Erstellen von Customer-Objekten:

public class CustomerBuilder {
    protected long id;

    public CustomerBuilder id(long id){
        this.id = id;
        return this;
    }
}

Der ImprovedCustomerBuilder zum Erstellen von Instanzen der ImprovedCustomer-Klasse:

public class ImprovedCustomerBuilder extends CustomerBuilder {
    private String email, name;

    public ImprovedCustomerBuilder name(String name){
        this.name = name;
        return this;
    }

    public ImprovedCustomerBuilder email(String email){
        this.email = email;
        return this;
    }

    public ImprovedCustomer build(){
        return new ImprovedCustomer(super.id, 
            this.name, this.email);
    }
}

So weit, so gut. Jeder, der mit Buildern arbeitet, kennt nun das folgende Problem beim Aufruf:

ImprovedCustomerBuilder builder = new ImprovedCustomerBuilder();
ImprovedCustomer ic = builder.id(3).
        name("Steffen").
        email("steffen.jacobs@example.org").
        build();

Fehler in Zeile 3: “Cannot resolve method ‘name(java.lang.String)'”. Das liegt daran, dass die id()-Methode ja in der Klasse CustomerBuilder definiert ist, welche this zurückgibt. Dieses Rückgabeobjekt ist damit vom Typ CustomerBuilder und nicht ImprovedCustomerBuilder und hat daher keine name()-Methode. Natürlich ist das echte Objekt auch vom Typ ImprovedCustomerBuilder, daher wäre an dieser Stelle ein Cast notwendig. Das weiß der Compiler allerdings leider nicht im Voraus.

Wenn man sich den Cast sparen möchte, kann der Rückgabetyp der id()-Methode in der Klasse CustomerBuilder in Manifold einfach mit @Self annotiert werden:

public class CustomerBuilder {
    protected long id;

    public @Self CustomerBuilder id(long id){
        this.id = id;
        return this;
    }
}

Und siehe da: Der Fehler verschwindet und das Programm funktioniert auch ohne Cast. @Self CustomerBuilder steht dabei für den konkreten Typ des Objektes this. Wenn this etwa eine andere, konkrete Klasse ist, die nur von CustomerBuilder erbt, wird diese stattdessen zurückgegeben. So auch in unserem Beispiel: Es wird ein Objekt vom Typ ImprovedCustomerBuilder zurückgegeben, wodurch der Fehler von oben nicht länger auftritt.

Die Verwendung von @Self erhöht insbesondere bei größeren Projekten mit einer dementsprechend größeren Vererbungshierarchie bei den Buildern signifikant die Lesbarkeit, da in diesen Fällen bei einem einzelnen Objektbuilderaufruf möglicherweise mehrere Casts auf einmal stattfinden müssten, damit der Compiler auch alle Methoden findet.

Short URL for this post: https://wp.me/p4nxik-3kQ
Steffen Jacobs

About Steffen Jacobs

Java Consultant & Developer at Orientation in Objects GmbH. Follow me on Twitter and find me on LinkedIn and Xing. Some of the source code associated with the blog articles can be found on GitHub.
This entry was posted in Java and Quality and tagged , , . Bookmark the permalink.

1 Response to Bessere Builder in Java mit Manifold: Self Type

  1. Avatar Scott says:

    Ausgezeichnete Serie auf Manifold! Danke vielmals!

Leave a Reply to Scott Cancel reply