Reputation: 4634
The question Switch over type in java considers how one can switch on code to be executed depending on an object's type.
Now I want to have a case
statement in the way it is used in many functional languages:
r = case x of
String s -> s
Int i -> toString i
That is, I want to switch on the type and also regard the switch statement as an expression (i.e., it should have a result).
Like in a switch, it should be allowed that multiple cases match but the evaluation will check from first to last and return the value of the first match (and null if there is no match).
It could be in the style of this answer to the aforementioned question which uses Java 8 functional features:
switchType(x,
caze(String.class, v -> print("String: " + v),
caze(Integer.class, v -> print("Int: " + v));
Upvotes: 1
Views: 259
Reputation: 4634
Similar to the adaptation in this answer which adapts the mentioned solution by executing only the first match, one can add an additional type parameter R
for the return type and use Function<T,R>
instead of Predicate<T>
:
public static <R> R caseType(Object obj, Function<Object, Optional<R>>... functions) {
for (Function<Object, Optional<R>> f : functions) {
Optional<R> res = f.apply(obj);
if (res.isPresent()) {
return res.get();
}
}
return null; // Default case (no match)
}
public static <T, R> Function<Object, Optional<R>> of(Class<T> cls, Function<T, R> f) {
// Wrap the function with a type check
return obj -> {
if (cls.isInstance(obj)) {
return Optional.of(f.apply((T) obj)); // As we must return a function taking an Object, we have to convert here
} else {
return Optional.empty();
}
};
}
The following examples demonstrate the usage and behaviour:
String s = "Hello World";
Integer i = 5;
Double d = 1.0 / 3;
Object x = s;
Object y = i;
Object z = d;
String res1 = caseType(x,
of(String.class, v -> v.substring(0, 5)),
of(Integer.class, v -> String.format("%d", v)),
of(Double.class, v -> String.format("%1.4f", v)));
String res2 = caseType(y,
of(String.class, v -> v.substring(0, 5)),
of(Integer.class, v -> String.format("%d", v)),
of(Double.class, v -> String.format("%1.4f", v)));
String res3 = caseType(z,
of(String.class, v -> v.substring(0, 5)),
of(Integer.class, v -> String.format("%d", v)),
of(Double.class, v -> String.format("%1.4f", v)));
String firstMatch = caseType(x,
of(String.class, v -> "first case"),
of(String.class, v -> "second case"));
String resNull = caseType(z,
of(String.class, v -> v.substring(0, 5)),
of(Integer.class, v -> String.format("%d", v)));
String resDefault = caseType(z,
of(String.class, v -> v.substring(0, 5)),
of(Integer.class, v -> String.format("%d", v)),
of(Object.class, v -> "unknown"));
String resSubtype1 = caseType(y,
of(Number.class, v -> String.format("%1.4f", v.doubleValue())),
of(Object.class, v -> "unknown"));
String resSubtype2 = caseType(z,
of(Number.class, v -> String.format("%1.4f", v.doubleValue())),
of(Object.class, v -> "unknown"));
System.out.println("res1: " + res1);
System.out.println("res2: " + res2);
System.out.println("res3: " + res3);
System.out.println("firstMatch: " + firstMatch);
System.out.println("resNull: " + resNull);
System.out.println("resDefault: " + resDefault);
System.out.println("resSubtype1: " + resSubtype1);
System.out.println("resSubtype2: " + resSubtype2);
Output:
res1: Hello
res2: 5
res3: 0.3333
firstMatch: first case
resNull: null
resDefault: unknown
resSubtype1: 5.0000
resSubtype2: 0.3333
If one wants the result to be an Optional
(with a value if there was a match and empty if there was no match) one can adapt the case function accordingly:
private static <R> Optional<R> caseTypeNonNull(Object obj, Function<Object, Optional<R>>... functions) {
for (Function<Object, Optional<R>> f : functions) {
Optional<R> res = f.apply(obj);
if (res.isPresent()) {
return Optional.of(res.get());
}
}
return Optional.empty(); // Default case (no match)
}
Upvotes: 3