Heh? What instance of polymorphism does it lack? Also, depending on what you mean by implicit types, Java has type inference nowadays.
Heh? What instance of polymorphism does it lack? Also, depending on what you mean by implicit types, Java has type inference nowadays.
1) Higher-kinded. List<Integer> is OK and List<T> is OK, but T<Integer> is not.
2) Dispatch to the right method based on param types. If your class exposes foo(Apple) and foo(Banana), it will not route a Fruit to either of those methods even if it is one of those specific types.
3) Return type polymorphism, which is probably the most relevant to the error-handling discussion, e.g. in this example:
fn greet() {
fail "not greeting today"
}
in another language, fail could be polymorphic over its return type - that is, if called from a function returning an Optional<>, fail itself could choose to return Optional.empty(). Or it could return the failed version of a CompletableFuture<>, or even throw a runtime exception depending on how the language was implemented.Edit: Not sure why the downvote.
This works in Java today with proper pattern matching (I left out the complete name from the permit clause, it won’t compile as is):
sealed interface <T,E> Result permits Ok, Error {
record Ok(T val) implements Result<T,E> {}
record Error(E err) implements Result<T,E> {}
}(and no, I have no idea why you're being downvoted, please take my upvote)
class Exception1 extends Exception {}
class Exception2 extends Exception {}
interface ThrowingFunction {
void apply() throws Exception1, Exception2;
}
class Foo {
public void doFoo(ThrowingFunction f) throws T {
f.apply();
}
public static void main() throws Exception1, Exception2 {
new Foo().doFoo(() -> {
if (false) {
throw new Exception1();
}
if (false) {
throw new Exception2();
}
});
}
}}
However, if you pass them through a generic then they will be flattened into the closest common ancestor: class Exception1 extends Exception {}
class Exception2 extends Exception {}
interface ThrowingFunction<T extends Throwable> {
void apply() throws T;
}
class Foo {
public <T extends Throwable> void doFoo(ThrowingFunction<T> f) throws T {
f.apply();
}
// Fails to compile, because Exception is still unhandled
public static void main() throws Exception1, Exception2 {
new Foo().doFoo(() -> {
if (false) {
throw new Exception1();
}
if (false) {
throw new Exception2();
}
});
}
}Is it possible to provide a map method that re-throws whatever is thrown by its argument, without widening?