我喜欢optionals现在在Java标准库中。但是我仍然遇到一个基本问题,我无法弄清楚如何以最好的方式解决(最容易阅读和理解,最漂亮,最短)的方式:
当可选项为空时如何从方法返回?
我正在寻找适用于选项数和代码块大小的不同组合的通用解决方案。
在以下示例中,我将尝试展示我的意思:
void m1() {
// When I get an optional:
Optional<String> o = getOptional();
// And want to return if it's empty
if (!o.isPresent()) return;
// In the whole rest of the method I have to call Optional.get
// every time I want the value:
System.out.println(o.get());
// Which is pretty ugly and verbose!
}
void m2() {
// If I instead return null if a value is absent:
String s = getNullabe();
if (s == null) return;
// Then I can use the value directly:
System.out.println(s);
}
这个问题是关于如何获得上述两个例子的好方面:可选的安全类型和可空类型的简洁。
其余的例子说明了这一点。
void m3() {
// If I on the other hand want to throw on empty that's pretty and compact:
String s = getOptional()
.orElseThrow(IllegalStateException::new);
System.out.println(s);
}
void m4() {
Optional<String> o = getOptional();
if (!o.isPresent()) return;
// I can of course declare a new variable for the un-optionalised string:
String s = o.get();
System.out.println(s);
// But the old variable still remains in scope for the whole method
// which is ugly and annoying.
System.out.println(o.get());
}
void m5() {
// This is compact and maybe pretty in some ways:
getOptional().ifPresent(s -> {
System.out.println(s);
// But the extra level of nesting is annoying and it feels
// wrong to write all the code in a big lambda.
getOtherOptional().ifPresent(i -> {
// Also, more optional values makes it really weird and
// pretty hard to read, while with nullables I would
// get no extra nesting, it would looks good and be
// easy to read.
System.out.println("i: " + i);
// It doesn't work in all cases either way.
});
});
}
Optional<String> getOptional() {
throw new UnsupportedOperationException();
}
Optional<Integer> getOtherOptional() {
throw new UnsupportedOperationException();
}
String getNullabe() {
throw new UnsupportedOperationException();
}
如果可选项为空,如何在方法的其余部分中不使用get
而不声明额外的变量并且没有额外的块嵌套级别,那么如何从方法返回?
或者,如果不可能得到所有这些,处理这种情况的最佳方法是什么?
答案 0 :(得分:19)
您可以使用orElse(null)
:
String o = getOptional().orElse(null);
if (o == null) {
return;
}
答案 1 :(得分:8)
您可以使用ifPresent
和map
方法,如果该功能无效并且您需要执行副作用,则可以使用ifPresent
,
optional.ifPresent(System.out::println);
如果另一个方法返回依赖于Optional,那么该方法可能还需要返回一个Optional并使用map方法
Optional<Integer> getLength(){
Optional<String> hi = Optional.of("hi");
return hi.map(String::length)
}
大多数当您致电isPresent
和get
时,您误导了Optional
。
答案 2 :(得分:5)
您正在使用的ifPresent并不要求您创建新的lambda,您只需使用方法参考:
getOptional().ifPresent(System.out::println);
但这并没有真正解决你想要以两个选项的存在为条件的情况。但作为
的替代方案// And want to return if it's empty if (!o.isPresent()) return;
为什么不改变条件,这在嵌套的情况下也很好用呢?没有必要明确表示回报:
if (o.isPresent()) {
System.out.println(o.get());
if (oo.isPresent()) {
System.out.println(oo.get());
}
}
然而,这种用例表明你并没有真正受益于Optional而不是可以为空的值。一般来说,如果你正在使用isPresent和get,那么Optional可能并没有真正让你得到那么多(除非它强迫你考虑缺少值的情况)。使用ifPresent,map,filter和其他更多功能&#34;方法可能是Optional值的更典型用法。
但无论如何,当您承诺选择时,请不要返回null。虽然在期望对象时返回null是完全合法的,但Optional的要点正是为了避免必须检查null。所以不要这样做:
Optional<String> getOptional() { return null; }
但改为:
Optional<String> getOptional() {
return Optional.empty();
}
否则你最终不得不这样做:
Optional<String> o = getOptional();
if (o != null && o.isPresent()) {
// ...
}
这真的只是两次做同样的事情。使用可选项,或使用可以为空的值,但不要同时执行这两项操作!
答案 3 :(得分:1)
那是一个很棒的话题,我们都喜欢编程的功能风格!
通常在启动方法的实现时,顶部会为您提供可选的权限。 此时,您开始怀疑,如何处理空的可选内容是最好的, 在这种情况下,只有退出并停止处理才有意义。
第1步-探索和分析
public void processMedia(String mediaClassName, String mediaName) {
// THAT MIGHT BE YOUR FIRST IDEA
MediaClass mediaClass = mediaClassFinder.find(mediaClassName).orElse(null);
// RETURNING ON NULL CONDITION LIKE THE BELOW CAN BE ALRIGHT,
// BUT POSSIBLY YOU CAN DO BETTER
if (mediaClass == null) {
return;
}
Optional<Media> media = mediaFinder.find(mediaClass.getId(), mediaName);
// do processing
// render the processed object
}
第2步 最好的方法可能是将实现的各个部分提取到单独的方法中,并以功能样式将它们链接在一起。 作为此练习的副作用,您可能最终会改善应用程序的界面和结构。 重构就是这样工作的。从下面看,没有显式的空分配,并且任何地方都没有额外的返回点。而且编码变得很有趣。
public void processMedia(String mediaClassName, String mediaName) {
mediaClassFinder.find(mediaClassName)
.flatMap(mediaClass -> mediaFinder.find(mediaClass.getId(), mediaName))
.map(this::compress)
.ifPresent(this::render);
}
private Media compress(Media media) {
// compress media implementation
return media;
}
private void render(Media media) {
// render media implementation
}
我希望你喜欢我的例子:)
答案 4 :(得分:0)
我不认为你提出的问题实际上是可行的,但我想建议只需将你的所有代码直接用于你的String并将其包装在一个函数中。所以你的功能就像这样:
void m4() {
Optional<String> o = getOptional();
if (!o.isPresent()) return;
doThings(o.get());
}
void doThings(String s){
System.out.println(s);
//do whatever else with the string.
}
这样,您只有范围内的字符串,并且每次要访问它时都不必调用.get()
。