7 ms·
> Java is a huge mess because it has no implicit types nor most instances of polymorphism Heh? What instance of polymorphism does it lack? Also, depending on w
by kaba0 2y ago
> Java is a huge mess because it has no implicit types nor most instances of polymorphism
Heh? What instance of polymorphism does it lack? Also, depending on what you mean by implicit types, Java has type inference nowadays.
- dllthomas 2y agoIIUC, for checked exceptions Java lacks parametric polymorphism (generics).
- kaba0 2y agoAh. But that’s also not true, you can do something like `throws T`.
- Yoric 2y agoI assume that the poster meant that Java could have had `Result<T, E>` return values like Rust if it had launched with parametric polymorphism, instead of having a layer of the type system dedicated to errors.
- kaba0 2y agoIt still can have it, see e.g. Vavr, or other functional libraries. 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> {} }
- mrkeen 2y agoMan I hope Vavr survives. It is an oasis of rationality and I'm disheartened that https://docs.vavr.io/ https://docs.vavr.io/ appears to be down. Its Set implementation actually has union and intersection (!!!)
- Yoric 2y agoSure, and it's great that this has become possible (a while ago now). My remark was about Java ~1.0, as by now, the ship has long sailed and it's unlikely that many people will start adopting this style. (and no, I have no idea why you're being downvoted, please take my upvote)
- Nullabillity 2y agoIt works, until it doesn't. Exceptions are the one place in Java that supports union types, if you have code that can throw both Exception1 and Exception2 then Java can keep track of whether they are thrown separately: 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(); } }); } }
- dllthomas 2y agoAh, neat! I think the last time I looked you couldn't, but it was ages ago; I'm not surprised to have misremembered and even less surprised if it's changed. Is it possible to provide a map method that re-throws whatever is thrown by its argument, without widening?
- mrkeen 2y ago> What instance of polymorphism does it lack? 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.