我通常对Java语言的设计非常满意,但今天我遇到了一个奇怪的设计选择。 我不知道您可以建议更改版本9或10的Java语言的任何平台,因此我在这里写这个问题。
如果值为null
,则使用Optionals可以更好地控制行为。
今天我将流中的值映射到Optional
。
我真的很惊讶,findFirst
然后给了Optional<Optional<T>>
而不只是Optional<T>
。
Optional<Optional<T>>
对于Java 9和Streams,这可以通过.flatMap(Optional::stream)
解决,但这只是一个解决方法和样板代码。
如果Optionals通常不会堆叠并且总是平缓下来会不会更好?
Optional<Optional<T>> => Optional<T>
Optional.of(Optional.of("")) => Optional.of("")
进一步解释:
如果子可选项为null或实际对象为null
,则使用Optionals并不关心。它总是归结为Optional.empty()
。
String s = null;
Optional<String> opt = null;
Optional.of(s) => Optional.empty()
Optional.of(opt) => Optional.empty()
此外,如果您正在使用选项,您只对该对象感兴趣。因此,如果您尝试获取对象,则必须双倍获取并检查可选项。
if(optionalOfOptional.isPresent()) {
optional = optionalOfOptional.get();
if(optional.isPresent()) {
objectOfInterest = optional.get();
}
}
我甚至认为这是错误的来源,因为如果你的对象存在条件,你总是需要检查两个选项。
if(optionalOfOptional.isPresent() && optionalOfOptional.get().isPresent()) {
...
}
只检查第一个选项很容易导致错误。
此外,为什么首先拥有null
的Optional是有意义的?我不能选择摆脱使用null
吗?
在方法层面,这可以肯定地解决。
例如Optional.of
可能类似于:
public static <T> Optional<T> of(T value) {
return value instanceof Optional ? value : new Optional<>(value);
}
在类型级别上,这可能并不容易。
答案 0 :(得分:4)
禁止Optional<Optional<T>>
只是没有意义。
假设你有一个List<Optional<T>>
。以下列表不同:
List<Optional<T>> listA = Arrays.asList(Optional.empty());
List<Optional<T>> listB = Arrays.asList();
它们显然是不同的:第一个是非空的,第二个是空的。
相应地,在他们的流上调用findFirst()
的结果是不同的:
System.out.println(listA.stream().findFirst().isPresent()); // true
System.out.println(listB.stream().findFirst().isPresent()); // false
现在,我知道Java无法知道的那些列表中数据的含义。假设列表包含调用Callable<Optional<T>>
列表的结果。在这种情况下,可能与我的应用程序之间的重要区别:
Optional.empty()
所以我真的不希望语言或API假设两者之间没有区别,并将Optional<Optional<T>>
转换为Optional<T>
。
由于List<E>.stream().findFirst()
总是返回Optional<E>
,因此无法阻止Optional<Optional<T>>
而不会阻止我创建List<Optional<T>>
(或Collection
,Set
,HashMap<K, Optional<T>>
等)。所以,你基本上必须完全禁止嵌套泛型,这将是一个重大的功能损失。
我对Optional<Optional<T>>
的存在非常满意,因为它与语言的其余部分完全一致。如果您不喜欢它们,请避免创建它们;但不要认为这对其他人都是正确的。
答案 1 :(得分:1)
我在其他地方谈到an Optional<T>
should never be null.它违反了Optional<T>
承诺:摆脱NullPointerException
s。
因此,我认为在Optional API中设置不足以允许Optional<Optional<T>>
。给定Optional时,获取Optional实例的方法应该表现得更好。但话虽如此,他们不能。如果Optional.of超载除了一个小问题,它们就可以完成。
由于您可以使用类型为Optional的泛型类型T,因此不能有两个Optional.of方法(一个用于Optional,一个用于T)。如果有人有一个明智的想法,它会阻止某些代码编译Optional<Optional<T>>
。拿下面的代码,它将无法编译。
package otherCode;
public class Abc<T> {
public static void main(String[] args) {
Abc<B> test1 = new Abc<>();
test1.foo(new A());
test1.foo(new B());
Abc<Base> test2 = new Abc<>();
test2.foo(new A());
test2.foo(new B());
Abc<A> test3 = new Abc<>();
// these two don't compile
test3.foo(new A());
test3.foo(new B());
}
public void foo(A item) {
System.out.println("Foo");
}
public void foo(T item) {
System.out.println("bar");
}
public static class Base {
}
public static class A extends Base {
}
public static class B extends Base {
}
}
不强制对整个语言进行语言和语法更改(禁止Optional<Optional<T>>
),自动转换并非易事。
Java 8一般我发现需要开发人员一段时间才能掌握。当它第一次出现时,Map<String, Optional<T>>
当Map<String, T>
已经足够或我看到Optional<Optional<T>>
时,我会看到Map<Optional<T>, Optional<Set<Optional<T>>>>
。我甚至看到了Map<T, Set<T>>
,而不仅仅是Optional<Optional<T>>
。随着时间的推移和人们对语言的斗争,我觉得我们学会了更好地管理这些打字并获得更明智的打字。
我认为不幸的是,Java不会自动进行转换,但对于可能需要Stream<Optional<T>>.first
一天的穷人,我愿意使用map,flatMap,或Else,以及of null每隔一段时间就能保持我的打字理智。使用这些方法可能有助于解决您的特定困境。
修改强>
我看到,无论是错过了还是编辑,OP都看到Optional<Optional<T>>
返回class B(val x: A) extends AnyVal
。 OP打算做什么会影响妥协。他们可以.map流图形去除内部可选或他们可以.filter&amp; .map将流去掉空的Optionals。一个令人遗憾的额外代码。