Ein Klassen-Matcher in Java


Es ist unglaublich, aber ich habe tatsächlich noch keinen Klassen-Matcher in meinem Blog behandelt.

Wenn man öfter mal basierend auf dem Typ eines Wertes verzweigen muss, sieht es oft so aus:

Object obj = ...
String s;
if (obj instanceof Integer) {
    s = "int " + (Integer) obj;
} else if (obj instanceof Long) {
    s = "long " + (Long) obj;
} else if (obj instanceof Double) {
    s = "double" + (Double) obj;
} else {
    s = "häh?!?";
}

Oder noch schlimmer, wenn man sich „sicher“ ist, dass es nur zwei Alternativen gibt:

String s = obj instanceof Integer 
    ? "int " + (Integer) obj
    : "long " + (Long) obj; 

…was natürlich mit einem niedlichen kleinen Atompilz explodiert, wenn dann doch etwas anderes in obj steckt.

Wie wäre es dagegen mit dieser Syntax hier:

String s = match(obj,
            when(Integer.class, i -> "int " + i),
            when(Long.class, l -> "long " + l),
            when(Double.class, d -> "double " + d)
        ).orElse("häh?!?");

Die match-Methode liefert ein Optional, so dass man leicht selbst entscheiden kann, wie man auf einen unbekannten Typ reagiert. Die Argumente in den Lambdas sind natürlich typsicher, das i ist z.B. vom Typ Integer. Im Ausgangsbeispiel hat man ein Problem, wenn instanceof-Check und Typcast nicht zusammenpassen – das kann hier nicht passieren.

Die DSL-Klasse, die das ermöglicht, ist erstaunlich kurz:

import java.util.Arrays;
import java.util.Optional;
import java.util.function.Function;

public final class InstanceOf {

    @SafeVarargs
    public static <R> Optional<R> match(Object o, Function<Object, Optional<R>>... cases) {
        return Arrays.stream(cases)
                     .map(c -> c.apply(o))
                     .filter(Optional::isPresent)
                     .findFirst()
                     .flatMap(Function.identity());
    }

    public static <T, R> Function<Object, Optional<R>> when(Class<T> clazz, Function<T, R> fn) {
        return obj -> clazz.isAssignableFrom(obj.getClass())
                        ? Optional.of(fn.apply((T) obj))
                        : Optional.empty();
    }
}

Eigentlich eine Schande, dass man so etwas nicht gleich in Java 8 eingebaut hat.

Dann wünsche ich viel Spaß beim typsicheren Kaskadieren!

Advertisements

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden /  Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden /  Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden /  Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden /  Ändern )

w

Verbinde mit %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.