为什么Java 8 Optional被实现为final,没有Some和None层次结构?

时间:2017-08-05 09:52:09

标签: java java-8 optional

在Java中,Optional实现为public final class Optional<T> { ... },而不是SomeNone的密封层次结构。

为什么不是这种情况?这是Java中缺少sealed的解决方法吗?背后有更深层次的推理吗?

如果你看看方法实现,你会看到通过这种方式它具有丑陋的空检查:

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));
    }
}

他们不仅丑陋,而且如果你有更长的方法链,每次调用都需要评估isPresent,即使Optional从一开始就是空的。

如果我们可以在链中传递固定的实现,则可以避免。

optional
  .map(i -> i) // isPresent()
  .map(Object::toString) // isPresent()
  .map(String::length) // isPresent()
  .map(...) // isPresent()

问题

为什么没有用于模拟空和非空案例的子类型?

我并没有特别要求为什么 Optional是最终的,而不是为什么它没有与SomeNone一起实施,正如许多其他语言一样,所以Why is optional declared as a final class并没有真正帮助。

2 个答案:

答案 0 :(得分:21)

The final modifier 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


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

So what does this have to do with Optional?

Optional is a value-based class.

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.

The boxed form of a value type should share the same attributes as value types (similar to Integer for int), which include preventing traditional classes from subtyping.

答案 1 :(得分:1)

事实上,这个问题并不新鲜。 Natpryce在他的书中提出了这个问题:Growing Object Oriented SoftwareMaybe更像是java.util.Optional,但它通过多态表示2个状态。实际上,它比Optional更快,因为它只应用State Patternpresent状态转换为absent状态一次。这意味着如果当前状态为absent,则以下状态始终为absent

但我想说另一个场景是面向对象的原则:Law of Demeter

  
      
  • 每个单元应该只有有限的知识关于其他单位:只有单位&#34; 密切&#34;与当前单位有关。
  •   
  • 每个单元只应与朋友交谈;不要与陌生人谈话
  •   
  • 只与您的直接朋友交谈。
  •   

正如您所看到的,LoD原则将避免您编写这样的train wreck代码,这使得代码紧密耦合&amp;打破封装,使其更难改变&amp;维护。

简而言之,如果您根据LoD原则,则不应在程序中编写任何map(one).map(another).map(...)链调用。从这个角度来看,引入一个继承层次来表示它的内部状态是没有好处的。如果你引入一个新状态并且更难以进行调配,那么State Pattern就更难维持。

然后Optional只有Maybe进行额外检查。一个是中间操作map&amp; flatMap,另一个是终端操作orElseorElseGet和.etc。因此,在Optional中应用State Pattern没有什么好处。