Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is Java 8 Optional implemented as final, without Some and None hierarchy?

Tags:

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()

Question

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.

like image 226
Grzegorz Piwowarek Avatar asked Aug 05 '17 09:08

Grzegorz Piwowarek


2 Answers

Summary

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:

Value Types for Java


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.


So what does this have to do with 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.


Why do we need value types in Java?

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.

Implementing Value Types

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).

like image 125
Vince Avatar answered Oct 21 '22 12:10

Vince


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.

like image 25
holi-java Avatar answered Oct 21 '22 12:10

holi-java