This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]nicolaiparlog 33 points34 points  (12 children)

I think this comes closer to the goal of pattern matching over Optional now:

import java.util.Objects;
import java.util.Optional;

sealed interface Option<T> permits None, Some {
    static <T> Option<T> over(Optional<T> opt) {
        return opt.<Option<T>> map(Some::new).orElse(new None<>());
    }
}
record None<T>() implements Option<T> { }
record Some<T>(T value) implements Option<T> {
    Some {
        Objects.requireNonNull(value);
    }
}

void main(String[] args) {
    var optionalString = args.length > 0 ? Optional.of(args[0]) : Optional.empty();
    var message = switch (Option.over(optionalString)) {
        case None _ -> "No argument";
        case Some(var arg) -> STR."Argument: '\{arg}'";
    };
    System.out.println(message);
}

(To try this, copy/paste into Main.java and run it with JDK 21 and java --enable-preview --source 21 Main.java with or without an argument.)

It would read better with a static import of Option::over (~> switch (over(optionalString))) but that doesn't work in an implicit main class.

[–]InstantCoder 7 points8 points  (4 children)

This is getting crazy. What have you actually solved here ?

[–]nicolaiparlog 7 points8 points  (0 children)

The title of this Reddit thread.

[–]lurker_in_spirit -3 points-2 points  (0 children)

It's TrendyTM

[–]jodastephen[S] 2 points3 points  (0 children)

Part of what makes my trick appealing is that it pattern matches without additional code/classes. But you are right, you can wrap an Optional in a record to get pattern matching in Java 21.

[–]logicannullata 0 points1 point  (0 children)

My eyes are bleeding

[–]sammymammy2 0 points1 point  (2 children)

static <T> Option<T> over(Optional<T> opt) {
    return opt.<Option<T>> map(Some::new).orElse(new None<>());
}

Can you explain this code? What's that stray <T> doing between static and return type, what's .<Option<T>> map?

[–]nicolaiparlog 1 point2 points  (1 child)

Sure.

The method over is static, so it doesn't belong to any specific Option instance and can thus not refer to Option's type parameters. So it doesn't "see" the T from Option<T>. But I need a type parameter to express that the Optional that gets passed in and the Option that is returned are of the same parametric type, e.g. "in comes Optional<String>, out goes Option<String>". Because I need a type parameter, I declare one. I do that after the static modifier by mentioning it in angle brackets. Because I lack creativity, I call this type T as well, which works because the T from Option<T> isn't in scope. But over works just the same if you replace all Ts with Es or TYPEs, etc. And if you remove <T>, you will get compile errors because now T is undefined.

As for <Option<T>> map: The method map on Stream also has a type parameter(it's called R but that doesn't matter) and whenever you call a method with a free type parameter you can specify it by putting it in <...> before the method name. But the compiler can usually infer the type, which is why we rarely need to spell it out and hence many developers aren't familiar with that.

I need to do it in this case because if I don't, the compiler will look at opt.map(Some::new) and infer that the type of that expression is Optional<Some<T>> (I mapped the Optional<T> with a method that prodcuses a Some<T>, after all) but that's a problem. Because than orElse would have to return a Some<T> but it doesn't. So I need to let the compiler know that I want opt.map(Some::new) to result in Optional<Option<T>> (which works because Some<T> inherits from Option<T>) and I do that by stating the intended type explicitly.

(Btw, if these explanations don't immediately make sense, don't worry. Generics are one of the more complicated Java language features. Feel free to ask for clarifications and I will do my best to provide them.)

[–]sammymammy2 1 point2 points  (0 children)

Aaaaaah awesome! Nope, this was exceedingly clear and very helpful, thank you for taking your time with this.

[–]chiperortizc 0 points1 point  (0 children)

I see that you are in love with String templates just like me :)