java可选参数_Java:可选的可选实现

java可选参数

java可选参数

java.util.Optional被实现为单个不可变的具体类,该类在内部处理两种情况。 一个有元素,一个没有元素。 让Optional作为一个接口并让两个不同的实现代替实现该接口不是更好的选择吗? 毕竟,这就是我们通常被教导要使用的一种面向对象的语言。

在本文中,我们将了解当前Optional实现的一些潜在参数。 我们还将学习为什么以不同的方式实现Streams,从而使Streams可以从文件甚至数据库表中获取。

真正的可选实现

真正的java.util.Optional::get实现如下所示:

 public T get() {

        if (value == null ) {

            throw new NoSuchElementException( "No value present" );

        }

        return value;

    }

可以看出,有两个代码路径。 一种是值是null(没有元素,并且不会引发异常),另一种是值是其他值(返回值)。

可选可选实现

让我们假装我们将回到时光机中,并被要求再次实现Optional 。 我认为我们中的许多人可能会想出一个初始解决方案,就像下面的解决方案(我将其命名为假设接口Option以便我们可以将其与“实际”接口进行区分)有两个截然不同的实现(此处为EmptyOptionPresentOption ):

 public interface Option<T> { 
    T get();

    boolean isPresent(); 
    public <U> Option<U> map(Function<? super T, ? extends U> mapper); 
    static <T> Option<T> empty() { return (Option<T>) EmptyOption.EMPTY; } (Option<T>) EmptyOption.EMPTY; } 
    <T> Option<T> of(T value) { static <T> Option<T> of(T value) { return new PresentOption<>(value); } PresentOption<>(value); } 
    <T> Option<T> ofNullable(T value) { static <T> Option<T> ofNullable(T value) {

        return value == null ? empty() : of(value); ? empty() : of(value);

    }
 }
 final class EmptyOption<T> implements Option<T> { 
    static final EmptyOption<?> EMPTY = new EmptyOption<>(); 
    private EmptyOption() {} 
    @Override public T get() { throw new NoSuchElementException(); } NoSuchElementException(); } 
    @Override public boolean isPresent() { return false ; } ; } 
    @Override

    public <U> Option<U> map(Function<? super T, ? extends U> mapper) {

        requireNonNull(mapper);

        return (Option<U>) EMPTY;

    }
 }
 final class PresentOption<T> implements Option<T> { 
    private final T value; 
    PresentOption(T value) { this .value = requireNonNull(value); } .value = requireNonNull(value); } 
    @Override public T get() { return value; } value; } 
    @Override

    public boolean isPresent() { return true ; } ; } 
    @Override

    public <U> Option<U> map(Function<? super T, ? extends U> mapper) {

        requireNonNull(mapper);

        return Option.ofNullable(mapper.apply(value));

    }
 }

为了简洁起见,仅示出了几种方法,但是原理保持不变:针对存在元素和不存在元素的情况的不同实现。 这给出了更清晰的代码,也使任何人都可以实现可选选项。

分析

我有信心在构思Optional时,JDK团队已对这种解决方案进行了评估,并且我认为不选择该解决方案是明智的决定。 Optional主要目的是“包装”返回值,以防止NPE和返回原始空值的其他缺点。 我还认为设计目标是使用Optional对性能的影响应该很小或可以忽略不计。

在下文中,我推测了一些论点,以选出上述的可选实施方案。

剖面污染

JIT编译器按需编译Java字节码,以提高解释字节码的性能。

为了有效地做到这一点,JIT编译器能够收集每种已知方法的统计信息。 每个方法都可以具有一个MethodData对象,该对象包含有关如何使用该方法的度量,并且一旦JVM认为该方法足够“温暖”(即在某种意义上已被充分调用),便会创建该对象。

创建和维护MethodData过程称为“分析”。

当调用之间使用的方法大不相同时,就会发生“配置文件污染”,包括但不限于提供交替的非null / null元素并调用不同的多态方法(例如,参数是T类型的泛型,并且被调用的方法调用T::equals )。 Java的基本功能是其动态调用方法的能力。 因此,当调用Option::get时, EmptyOption::get或最终调用PresentOption::get取决于调用时存在的实现。

一旦该方法被调用了10,000次,JIT编译器就会使用MethodData创建一个有效的已编译代码段,根据到目前为止收集到的统计信息,该代码段将以最佳方式执行。

因此,如果元素始终存在(使用PresentOption ),并且牢记这一点进行编译,但是随后突然出现EmptyOption ,则代码必须“退出”并采用慢得多的代码路径。

仅在最后一个类中使用Optional ,就不可能再有Optional方法的任何其他实现,因此,不会因不同的实现而造成配置文件污染。 JIT可以确定性且合理地快速确定编译的代码。

但是,等等,JVM不可能在启动时检查所有类并确定实际上只有两个实现类。Option ,然后它可以解决整个问题? 好吧,不。 我们可以随时随意添加类,因此无法安全地枚举特定接口的所有可能实现。 至少要等到我们有了真正的Java密封类时,才能进行。

原料药污染

如果人们可以自由编写Optional自定义实现,那么与内置Optional相比,这些实现很可能会遭受设计缺陷/偏差。 同样,人们可能会让他们自己的类型实现Optional接口,这增加了JIT编译器/分析器的负担,并因此诱使人们使用非预期的复合类型(例如Foo implements Bar, Optional<Bazz>)

而且, Optional现在是Java不可或缺的一部分,因此,可以使它与JDK本身一起有效发展,包括内联类和即将推出的其他新Java功能。

可选与流

Optional相反, java.util.stream.Stream和专用版本(例如IntStream )确实是接口。 为什么Stream不像Optional那样具体地进行最后的上课?

好吧,Streams有一套完全不同的要求。 可以从Collection或数组中获取Stream但是有很多更强大的获取Stream 。 可以从文件,套接字,随机生成器甚至从数据库表中获取Stream 。 如果Stream被密封,将无法实现这些功能。

Speedment Stream是一个库的示例,该库允许从几乎任何数据库中获取标准Java流。 在此处阅读有关Speedment Stream的更多信息。

结论

Optional密封,有充分的理由。 Optional的内部实现不太清楚,但这是值得付出的代价,它具有更好的性能和更清晰的用户代码。

流是非密封接口,任何人都可以实现,并可用于从各种来源(包括文件和数据库表)获取元素。 Speedment Stream ORM可用于从数据库表中获取Streams。

在此处下载Speedment Stream。

翻译自: https://www.javacodegeeks.com/2019/08/java-optional-implementation-optional.html

java可选参数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值