在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类型(以及OptionalInt , OptionalLong , OptionalDouble类型-稍后将进一步介绍这些类型)
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
仍然是一个非常有用的添加的主要原因之一是,由于我们还具有OptionalInt , OptionalLong , OptionalDouble类型,因此“对象流”和“原始流”具有“统一的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上阅读以下主题:
- / r / java
- / r /编程
请继续关注本博客系列中发布的更多令人兴奋的Java 8内容。
翻译自: https://www.javacodegeeks.com/2014/04/java-8-friday-optional-will-remain-an-option-in-java.html