根据我在 Java 世界的经验,通常避免使用
Optional
作为函数的参数,而是将其用作返回类型(相关的 SO 问题)
我最近开始熟悉 Scala,看到了
Option
作为方法参数类型的许多用法,例如
def myTestFunction(x: Option[Int]): Int
。 Java 变体是 int myTestFunction(Optional<Integer> x)
我不明白 Scala 变体如何解决 Java 变体中可能出现的所有问题(例如将
null
作为 Option
/Optional
值传递)。
我的理解是否正确,使用或不使用
Option
/Optional
作为方法参数只是一个品味问题,了解可能的缺点?
Optional 不能自由调整自身,这意味着您无法编写代码。 Scala 具有隐式和使用站点泛型协同/逆变配置(java 没有的 2 个东西),这大概提供了一些空间来明智地将
Option(al)
粘贴到 scala 中的参数类型中,而这在 java 中并不是一个好的主意。
想象一下你有一个方法,它接受一个数字列表,并且我不知道,将它们的平均值计算为双精度。在这里:
double avg(List<Number> ns) {
double v = 0.0;
for (Number n : ns) v += n.doubleValue();
return v / ns.size();
}
// There's plenty to improve here. The point isn't
// an esoteric deep-dive on how to average numbers
// as accurately as possible. It's just an example.
你有一个整数列表,嘿,这些都是数字,所以,你调用这个方法:
List<Integer> myNums = List.of(1, 2, 3, 4);
System.out.println(avg(myNums));
// Expecting to see: 2.5
除非你看不到这一点。它甚至无法编译。编译器抱怨你需要一个
List<Number>
,而 List<Integer>
显然不算数。编译器是正确的,因为方法 could 执行 ns.add(5.5)
会在整数列表中粘贴一个 double ,这是错误的。事实并非如此,但这不是它的工作原理:签名(double avg(List<Number> ns)
定义了方法可以做什么。编译器确保所有内容都有意义对于合法允许该方法做的所有可能的事情。它是什么实际上是无关紧要的。它可以做add
的事情,因此,你不能调用它。
让我们解决这个问题!
double avg(List<? extends Number> ns) {
... everything the same ...
}
现在一切都很棒。在幕后发生的事情是,我们告诉编译器主动阻止任何调用
ns.add(5.5)
的尝试,事实上,如果您尝试这样做,编译器会抱怨您无法在 .add
上调用 List<? extends Number>
。完全没有。唯一可以用字面意思来称呼它的是 null
,因为该表达式同时是每个引用类型。这是学术性的; ns.add(null)
有点没用。
Optional 在这一点上做得不太好。
相同的原理,略有不同的故事:
我有一个方法可以搜索第一个“适合”给定正则表达式的元素。如果找到,则复制该元素并将其添加到列表末尾。由于某种原因,null 被认为与 null 匹配。如果没有找到这样的元素,则不会发生任何事情:
void dupeFirstMatch(List<String> ts, Pattern regex) {
for (String t : ts) {
if (regex.matcher(t).matches()) {
ts.add(t);
return;
}
}
}
现在让我们尝试在其中混合可选性,就像我们注入
? extends
来“使其对任何类型的数字起作用”一样,我们现在想要注入一些东西来解决上面代码中的以下问题:此方法 works无论可选性如何。如果我们尝试在 Optional<String>>
列表上执行此操作,我们就会陷入困境!然而,这个操作的本质意味着我们应该能够做到这一点:该操作对“可选字符串”和“明确存在的字符串”同样有意义。毕竟,我们首先只添加从列表中获得的元素,因此它不可能是“无值”,除非输入允许无值。
换句话说,如果我们有一个方法:
NONE
/null
...那么可选性并不重要。任何一个都可以。
但是你无法用
Optional
来表达这一点。没有办法拥有? extends
和朋友所做的事情。
这会导致
Optional<T>
从根本上出现问题。 即使你有代码可以处理它可能包装一个NONE
的概念,你也无法表达它。因此,即使您不想并且业务逻辑不需要它,您也被迫展开所有内容。
因此,尽快展开你的可选参数,并且使用可选参数类型的方法与该规则是对立的,因此,你不应该拥有它们。
这解释了为什么 java 社区不喜欢它,并且应该给你一些帮助来寻找 scala 特定的功能来避免这个问题(或者,大的 scala 社区不介意这些;我不'不知道)。