In Java, Optional
is implemented as public final class Optional<T> { ... }
and not as a sealed hierarchy of Some
and None
.
Why is this not the case here? Is this a workaround for the absence of sealed
in Java? Is there any deeper reasoning behind it?
If you have a look at method implementations, you will see that by going this way cases it features ugly null checks:
public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Optional.ofNullable(mapper.apply(value));
}
}
They're not only ugly but if you have a longer method chain, isPresent
will need to be evaluated during every call, even if the Optional
is empty since the beginning.
If we could pass a fixed implementation down the chain, it could be avoided.
optional
.map(i -> i) // isPresent()
.map(Object::toString) // isPresent()
.map(String::length) // isPresent()
.map(...) // isPresent()
Why weren't subtypes used to model empty and non-empty cases?
I'm not specifically asking why Optional
is final, rather why it wasn't implemented with Some
and None
, as many other languages do, so Why is optional declared as a final class is not really helpful.
The final
modifier on class Optional
is there in preparation for a bigger feature: value types, a goal for Project Valhalla (features for Java 10+).
You can read all about value types for Java in the 2014 article linked below:
JEP 169 drafts the proposal for implementing value objects into Java.
Provide JVM infrastructure for working with immutable and reference-free objects, in support of efficient by-value computation with non-primitive types.
JEP 390 hints at the possibility of "migrating certain classes to become primitive classes":
The design and implementation of primitive classes is sufficiently mature that we can confidently anticipate migrating certain classes of the Java Platform to become primitive classes in a future release.
Optional
?In the 2014 paper, it's mentioned how value-based classes may act as the boxed version of value types:
In fact, it seems likely that the boxed form of every value type will be a value-based class.
Optional
is a value-based class.
In Java, objects instantiated from reference types have an identity. This allows specific objects to be referenced by variables and compared by reference. All classes/enum/interfaces currently create reference types. Thus, all objects have an identity.
But, in theory, not all objects require an identity, as explicitly mentioned in the 2014 paper linked above:
Object identity serves only to support mutability, where an object’s state can be mutated but remains the same intrinsic object.
Identities aren't free, and immutable types don't require mutation, which inheritly means they don't require identities.
Identities for immutable types result in an excessive footprint:
Object identity has footprint and performance costs, which is a major reason Java, unlike other many object oriented languages, has primitives.
James Gosling wrote an article back in 1999 about compiling immutable types to values:
It is almost possible, under the current language spec, for a sufficiently clever optimizing compiler to transform certain classes into lightweight objects that are not heap allocated and are passed by value rather than reference: declare the class and all its instance variables to be final.
This idea has been inherited by Oracle's experimental Project Valhalla, lead by Brian Goetz. In preparation, a specification for value-based classes has been created, which one of the requirements is for the class to be final
.
The 2014 paper on value types in Java further exposes the decision to enforce the final
requirement:
Can values participate in inheritance-based subtyping? No.
Can a value class be abstract or non-final? No.
The decision to limit or prohibit subclassing and subtyping of value types is necessary to avoid pointer polymorphism.
We can therefore ensure that all methods are resolved unambiguously in the exact type of the method receiver. Invoking a value method is always like invokestatic or invokespecial and never like invokevirtual or invokeinterface.
Value types cannot participate in traditional subtyping (if at all, it would be limited).
In fact, this question is not new. and it is proposed by Natpryce in his book: Growing Object Oriented Software. the Maybe is more like as java.util.Optional
but it represent 2 states by polymorphism. Indeed, it's faster than Optional
since it applies State Pattern to transit the present
state to absent
state only once. which means the following state is always absent
if the current state is absent
.
But I want to say another scenario the Object-Oriented Principle: Law of Demeter
- Each unit should have only limited knowledge about other units: only units "closely" related to the current unit.
- Each unit should only talk to its friends; don't talk to strangers.
- Only talk to your immediate friends.
As you can see the LoD principle will avoid you to writing such a train wreck code which makes the code tight-coupling & breaking encapsulation and make it harder to change & maintain.
In a nutshell, if you according to the LoD principle you shouldn't write any map(one).map(another).map(...)
chain calls in your program. from that point of view, there is no benefit to introduce an inheritance hierachy to represent its internal state. Since the State Pattern is harder to maintain if you introduce a new state and harder for debuging.
Then there are only 2 additional checking in the Optional
than Maybe. one is the intermediate operation map
& flatMap
, another is is the terminal operation orElse
, orElseGet
and .etc. So there is no great advantage to applies State Pattern in Optional
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With