I want to point out some of the potential headaches and issues regarding this re-implementation of Optional<T>
.
Here's how I would initially go about it:
public class Option<T> {
/* fields, constructors, other methods */
public <U> Option<U> flatten() {
if (isNone()) return None();
T unwrapped = this.unwrap();
if (unwrapped instanceof Option) {
return (Option<U>) unwrapped; //No type safety!
} else {
return (Option<U>) this;
}
}
}
However, this code is EVIL. Note the signature of <U> Option<U> flatten()
means that the U
is going to be type-inferenced into whatever it needs to be, not whatever a potential nested type is. So now, this is allowed:
Option<Option<Integer>> opt = /* some opt */;
Option<String> bad = opt.flatten();
Option<Option<?>> worse = opt.<Option<?>>flatten();
You will face a CCE upon using this for the other operations, but it allows a type of failure which I would say is dangerous at best. Note that any Optional<Optional<T>>
can have #flatMap
unwrap for you: someOpt.flatMap(Function.identity());
, however this again begs the question of what caused you to arrive at a wrapped optional to begin with.
Another answer (by @NathanielFord) notes the constructor as an option, which seems viable as well, but will still face the runtime check upon construction (with it simply being moved to the constructor):
public class Option<T> {
/* fields, constructors, other methods */
public Option<T>(T someValue) { ... }
public Option<T>(Option<T> wrapped) {
this(wrapped.isNone() ? EMPTY_OBJECT : wrapped.unwrap());
}
public Option<T> flatten() {
return this; //we're always flattened!
}
}
Note as well, the re-creation of Optional<T>
by
@E_net4thecommentflagger has the potential for a nasty future bug: Optional.ofNullable(null).isNone()
would return true
! This may not be what you want for some potential use-cases, and should #equals
be implemented in a similar manner, you'd end up with Optional.ofNullable(null).equals(Optional.None())
, which seems very counter-intuitive.
All of this to say, that while Rust may require you to deal with these nested optionals, you are writing code for Java, and many of the potential restrictions you faced before have changed.
>` even though the lists can definitely be compared. In Rust this is easy using `impl` blocks and trait bounds.