21

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.

Grzegorz Piwowarek
  • 13,172
  • 8
  • 62
  • 93
  • 1
    If the jsr doesn't say anything on that matter we can only speculate. – Fildor Aug 05 '17 at 09:55
  • Possible duplicate of [Why is Optional declared as a final class?](https://stackoverflow.com/questions/22734660/why-is-optionalt-declared-as-a-final-class) – Vince Aug 05 '17 at 09:57
  • @VinceEmigh no, it's not. This question is pretty much equal to "Why do we want to declare classes as final?". – Grzegorz Piwowarek Aug 05 '17 at 09:58
  • So [Use of final class](https://stackoverflow.com/questions/5181578/use-of-final-class-in-java)/[Good reasons to prohibit inheritance](https://stackoverflow.com/questions/218744/good-reasons-to-prohibit-inheritance-in-java)? I feel the first answer of the duplicate answers the question percisely: `Optional` is a value based type, and (by specification) value based types must be immutable. – Vince Aug 05 '17 at 10:02
  • 2
    @VinceEmigh I am not asking about the prohibition of inheritance. I want to know what was driving the reason to implement Optional without using any form of inheritance - which is a standard approach in Scala, Haskell and even Vavr library for Java. – Grzegorz Piwowarek Aug 05 '17 at 10:04
  • 4
    It's not a workaround for the absence of sealed. The constructor could very well be private, and the Some and None implemented as private static nested classes. I guess the implementation using if was simpler and faster than one based on polymorphism. – JB Nizet Aug 05 '17 at 10:06
  • 3
    @JBNizet This does not really convince me. By going this way, if you have N chained map() calls, it will evaluate `isPresent` N times and this could be avoided by going the Some/None way. – Grzegorz Piwowarek Aug 05 '17 at 10:18
  • @matoni no, all is encapsulated by different implementations of different types. For example, `Some.isPresent()` returns a fixed `true` and `None.isPresent()` returns a fixed `false` – Grzegorz Piwowarek Aug 05 '17 at 10:34
  • 10
    [Spec for value-based types](http://docs.oracle.com/javase/8/docs/api/java/lang/doc-files/ValueBased.html). A [paper on value-types](http://cr.openjdk.java.net/~jrose/values/values-0.html) (planned for [Valhalla](https://en.m.wikipedia.org/wiki/Project_Valhalla_(Java_language))) "*Can values participate in inheritance-based subtyping? No.*" - "*The decision to limit or prohibit subclassing and subtyping of value types is necessary to avoid pointer polymorphism.*" - It seems they're just sticking to their plans, possibly in regards to converting existing value-based classes to value types. – Vince Aug 05 '17 at 10:34
  • @VinceEmigh thanks, this is something I was looking for :) `Optional` is one of the candidates for being a Value Type – Grzegorz Piwowarek Aug 05 '17 at 10:37
  • @VinceEmigh consider making this an actual answer – Grzegorz Piwowarek Aug 05 '17 at 10:43

2 Answers2

22

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

Vince
  • 14,470
  • 7
  • 39
  • 84
  • this still makes no sense, there is no need for value type to be immutable... C# has mutable value types – Enerccio Jan 17 '18 at 09:18
  • @Enerccio It's not a requirement, rather than a preference. If a type doesn't require an identity, why give it one? It's a waste. As for C# allowing *mutable* value types, yes it's possible, but [frowned upon](https://stackoverflow.com/questions/7088108/in-c-sharp-are-value-types-mutable-or-immutable): "*One piece of advice: don't. Mutable value types are a bad idea, and should be avoided.*" – Vince Jan 17 '18 at 15:29
1

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.

holi-java
  • 29,655
  • 7
  • 72
  • 83
  • 5
    LoD focuses on [content coupling](https://en.m.wikipedia.org/wiki/Coupling_(computer_programming)#Types_of_coupling), where one object relies on the inner-workings of another object, resulting in tangled code. In this case, not only is `map` is returning a completely new `Optional`, but there's no exposure of the inner-workings of `Optional`, so even if `map` returned the same instance, it would be no different than making subsequent calls: `opt.map(...); opt.map(...); ...`. Don't confuse [fluent interfaces](https://en.m.wikipedia.org/wiki/Fluent_interface) with LoD violations. – Vince Aug 05 '17 at 18:02
  • @VinceEmigh thanks for your feedback. maybe I confused you. what I mean is the `map(a-> a.b).map(b->b.c).map(...)`, it will violate the LoD principle like as : `a.b.c...N`, :) – holi-java Aug 05 '17 at 18:26
  • 2
    That wouldn't be the fault of `Optional#map`, but the fault of the type the optional is wrapping. Nothing is forcing `a` to expose `b`, that was the choice of the user. – Vince Aug 05 '17 at 19:17
  • @VinceEmigh well, so I have said that in my answer what I said is: "*another scenario*". Indeed, I have already upvote your answer, I didn't argue your answer, I only want to describe my thought, so I reopen the question again. you talk about value object, and I talk about the LoD. :) – holi-java Aug 05 '17 at 19:19