Java 8 Friday:可选将保留为Java中的一个选项

Data Geekery ,我们喜欢Java。 而且,由于我们真的很喜欢jOOQ的流畅的API和查询DSL ,我们对Java 8将为我们的生态系统带来什么感到非常兴奋。

Java 8星期五

每个星期五,我们都会向您展示一些不错的教程风格的Java 8新功能,这些功能利用了lambda表达式,扩展方法和其他好东西。 您可以在GitHub上找到源代码

可选:Java中的新选项


到目前为止,Java 8的所有新增功能让我们非常激动。总而言之,这是一场革命,比以往任何时候都重要。 但是也有一两个疮点。 其中之一就是Java将永远不会真正摆脱

空:十亿美元的错误

在以前的博客文章中,我们已经解释了使用Ceylon语言处理NULL的优点, Ceylon语言找到了解决此问题的最佳解决方案之一-至少在注定永远支持空指针的JVM上。 在Ceylon中,可空性是一个标志,可以通过在类型名称后附加问号来将其添加到每种类型。 一个例子:

void hello() {
    String? name = process.arguments.first;
    String greeting;
    if (exists name) {
        greeting = "Hello, ``name``!";
    }
    else {
        greeting = "Hello, World!";
    }
    print(greeting);
}

真漂亮 结合流敏感的类型 ,您将永远不会再遇到可怕的NullPointerException

最近在手术室里。极客和戳

最近在手术室里。 极客和戳


其他语言也引入了Option类型。

最突出的是:Scala

Java 8现在还引入了Optional类型(以及OptionalIntOptionalLongOptionalDouble类型-稍后将进一步介绍这些类型)

Optional如何运作?

Optional背后的要点是包装一个Object并提供便利的API以流畅地处理可空性。 这与Java 8 lambda表达式配合得很好,该表达式允许延迟执行操作。 一个例子:

Optional<String> stringOrNot = Optional.of("123");

// This String reference will never be null
String alwaysAString =
    stringOrNot.orElse("");

// This Integer reference will be wrapped again
Optional<Integer> integerOrNot = 
    stringOrNot.map(Integer::parseInt);

// This int reference will never be null
int alwaysAnInt = stringOrNot
        .map(s -> Integer.parseInt(s))
        .orElse(0);

流畅的API,尤其是在新的Java 8 Streams API中具有上述优点,它广泛使用Optional 。 例如:

Arrays.asList(1, 2, 3)
      .stream()
      .findAny()
      .ifPresent(System.out::println);

上面的代码将把Stream中的任何数字打印到控制台上,但前提是存在这样的数字。

未对旧API进行改造

出于明显的向后兼容的原因,“旧API”未进行改装。 换句话说,与Scala不同,Java 8不在JDK上使用Optional 。 实际上,在Streams API中唯一使用Optional地方。 如您在Javadoc中所见,用法非常稀缺:

http://docs.oracle.com/javase/8/docs/api/java/util/class-use/Optional.html

这使Optional难以使用。 之前我们已经写过关于该主题的博客 。 具体而言,API中不存在Optional类型不能保证不可为空。 如果将Streams转换为collections并且将collections转换为streams,这尤其令人讨厌。

Java 8 Optional类型是危险的

参数多态性

Optional对它的“受感染” API的最坏影响是参数多态性,或者简称为:泛型。 当您对类型进行推理时,您将很快理解:

// This is a reference to a simple type:
Number s;

// This is a reference to a collection of
// the above simple type:
Collection<Number> c;

泛型通常用于通常被认为是合成的内容。 我们有一个Collection String 。 使用Optional ,会稍微滥用这种组合语义(在Scala和Java中都是如此)来“包装”可能为空的值。 现在,我们有:

// This is a reference to a nullable simple type:
Optional<Number> s;

// This is a reference to a collection of 
// possibly nullable simple types
Collection<Optional<Number>> c;

到目前为止,一切都很好。 我们可以替换类型以获得以下内容:

// This is a reference to a simple type:
T s;

// This is a reference to a collection of
// the above simple type:
Collection<T> c;

但现在输入通配符和使用地点差异。 我们可以写

// No variance can be applied to simple types:
T s;

// Variance can be applied to collections of
// simple types:
Collection<? extends T> source;
Collection<? super T> target;

Optional的上下文中,以上类型是什么意思? 直观地讲,我们希望这与Optional<? extends Number> Optional<? extends Number>Optional<? super Number> Optional<? super Number> 。 在上面的示例中,我们可以编写:

// Read a T-value from the source
T s = source.iterator().next();

// ... and put it into the target
target.add(s);

但这对Optional不再起作用

Collection<Optional<? extends T>> source;
Collection<Optional<? super T>> target;

// Read a value from the source
Optional<? extends T> s = source.iterator().next();

// ... cannot put it into the target
target.add(s); // Nope

…而且,当我们拥有Optional和更复杂的API时,没有其他方法可以推断出使用地点的差异。

如果将通用类型擦除添加到讨论中,情况会变得更糟。 我们不再删除上述Collection的组件类型,而实际上也删除了任何引用的类型。 从运行时/反射的角度来看,这几乎就像在各处使用Object一样!

即使对于简单的用例,泛型系统也非常复杂。 Optional只会使事情变得更糟。 很难将Optional与传统的集合API或其他API融合在一起。 与Ceylon的流敏感键入甚至Groovy的elvis运算符的易用性相比, Optional就像您的大锤。

将其应用于API时要小心!

原始类型

为什么Optional仍然是一个非常有用的添加的主要原因之一是,由于我们还具有OptionalIntOptionalLongOptionalDouble类型,因此“对象流”和“原始流”具有“统一的API”。

换句话说,如果您正在使用原始类型,则可以以几乎相同的方式切换流的构造并重用其余的流API使用源代码。 比较这两个链:

// Stream and Optional
Optional<Integer> anyInteger = 
Arrays.asList(1, 2, 3)
      .stream()
      .filter(i -> i % 2 == 0)
      .findAny();
anyInteger.ifPresent(System.out::println);

// IntStream and OptionalInt
OptionalInt anyInt =
Arrays.stream(new int[] {1, 2, 3})
      .filter(i -> i % 2 == 0)
      .findAny();
anyInt.ifPresent(System.out::println);

换句话说,鉴于在JDK API中很少使用这些新类型,因此这种类型在一般情况下的可疑用途(如果改型为非常向后兼容的环境)以及泛型擦除对Optional的影响,我们敢说

真正添加此类型的唯一原因是为引用和原始类型提供更统一的Streams API

太难了 令我们感到疑惑的是,我们是否最终应该完全摆脱原始类型。

哦还有

Optional不是可Serializable

不。 不可Serializable 。 例如,与ArrayList不同。 通常的原因:

在JDK可序列化中进行处理会使我们的维护成本急剧增加,因为这意味着该表示将一直冻结。 这限制了我们将来开发实现的能力,而我们无法轻松修复错误或提供增强功能的情况数量非常之多,而这种情况本来就很简单。 因此,尽管对您来说,这看起来像是一个“可序列化的实现”的简单问题,但不仅限于此。 解决早期的选择以使某些东西可序列化所消耗的工作量是惊人的。

引用Brian Goetz,来自: http//mail.openjdk.java.net/pipermail/jdk8-dev/2013-September/003276.html

想讨论Optional吗? 在reddit上阅读以下主题:

请继续关注本博客系列中发布的更多令人兴奋的Java 8内容。

翻译自: https://www.javacodegeeks.com/2014/04/java-8-friday-optional-will-remain-an-option-in-java.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值