Programming languages having rigid syntax is like English being limited to the Latin alphabet - the opportunities for creativity lie in the combinations of higher level structures.
<T> T match(Function<X, T> foo, BiFunction<Y, Z, T> bar);
* in the cases where I don't have control over the common base type, I've written a builder pattern that constructs a sequence of predicate-function pairs and applies them appropriately. This looks like so: new PatternMatchingHelper()
.append(Foo.class, foo -> doSomething(foo.x))
.append(Bar.class, bar -> doSomethingElse(bar.y, bar.z))
.otherwise(x -> fallbackValue())
.apply(objectOfUnknownType);
The main disadvantage here is that it doesn't work well with generics, because the class objects have raw types.