【问题标题】:Java8 : stream findFirst resultJava8:流 findFirst 结果
【发布时间】:2017-11-11 12:22:42
【问题描述】:

我想知道是否有办法在不使用 .orElse() 的情况下摆脱 findFirst().get() 的警告,因为我知道 100% 每次都有结果,所以我从来没有得到 NoSuchElementException

例如让我们看看下面的代码:

    List<String> myList = new ArrayList<>();
    myList.add("Test");
    myList.add("Example");
    myList.add("Sth");

    String fisrstString = myList.stream().findFirst().get(); // here I surely get "Test" 

我不知道其他 IDE 如何处理这个问题,但 IntelliJ 将其视为警告

'Optional.get()' 没有'isPresent()'

我想它可能不知道你什么时候可以得到NoSuchElementException,什么时候没有,或者我不知道为什么。我知道有一些方法可以解决这个警告(isPresent() 检查,.orElse(something)),但代码无用,所以我不想使用这些解决方案,因为它们太没必要了。

您知道我能做什么,或者解释一下 IDE 是如何处理的吗?

【问题讨论】:

  • 我认为有一个选项可以禁用 IntelliJ 所做的某些检查。尝试在设置中寻找它
  • 您可以毫无问题地流式传输一个空列表,但是如果您尝试在一个空列表中获取 1s 元素,您将得到一个 NoSuchElementException
  • 我知道有可能禁用该警告,但如果我禁用,即使我需要该通知,它也不会再通知我。
  • 使用myList.get(0)...

标签: java intellij-idea java-8 java-stream optional


【解决方案1】:

嗯,就我而言,最好的方法是使用函数式编程并继续使用可选。因此,例如,如果您需要将此字符串传递给某个服务,您可以这样做:

String fisrstString = myList.stream().findFirst().get();
service.doSomething(fisrstString);

但这看起来不太好。相反,您可以利用函数式编程的优点,并这样做:

myList.stream().findFirst().ifPresent(service::doSomething);

【讨论】:

  • 但是我不想打印它,我只是用字符串写的,因为它非常简单明了我想要什么,但是例如,如果我想将结果传递给我不能做的方法这样,我必须使用 get 警告。
  • @Sunflame 您可以将结果传递给ifPresent 内部的方法。 .ifPresent(result -&gt; doSomething(result));
  • @Sunflame Optional 的 monad 的想法来自函数式编程。所以对我来说,这种情况下最好选择 FP 的专业人士
  • 我使用了你的解决方案,在ifPresent 中也有一个检查是否为空。目前我认为这是最好的解决方案,因为我不必像.orElse(StringUtils.EMPTY) 或类似的东西添加任何额外的未使用值
  • 其他人可能会想知道如果元素不存在会发生什么。我认为实际上断言问题中的主张会是更好的方法
【解决方案2】:

您应该使用findFirst() 返回的Optional,而不是尝试获取它的值(如果它确实存在的话)。

myList.stream()
    .findFirst()
    .ifPresent(/* consume the string here, if present */);

Optional.ifPresent 方法接收 Consumer,仅当 Optional 包含非空值时才会使用。

问题在于,我们 Java 开发人员已经习惯了命令式范式......特别是,我们习惯于获取一个对象并推送它,即一个方法:

String myString = "hello"; // getting an object here

System.out.println(myString); // pushing the object to System.out here
                              // (via the println method)

使用Stream.findFirst() 返回的Optional,您的操作与上述相同:

String myString = myList.stream()
    .findFirst()
    .get(); // getting a string here

System.out.println(myString); // pushing the string here

另一方面,功能范式(包括Optional)通常以另一种方式工作:

myList.stream()
    .findFirst()
    .ifPresent(myString -> System.out.println(myString));

在这里,您不会获取字符串然后将其推送到某个方法。相反,您为OptionalifPresent 操作提供一个参数,并让Optional 的实现将值推送到您的参数。换句话说,您通过ifPresent 的参数Optional 包装的值。然后ifPresent 将使用这个Consumer 参数,前提是该值存在。

这种拉取模式在函数式编程中很常见,一旦你习惯了它就非常有用。它只是要求我们开发人员开始以不同的方式思考(和编程)。

【讨论】:

  • 谢谢你的解释,你是对的,我和你说的有同样的经历,我更喜欢将对象推送到关心它的方法,我必须开始思考一点不同,在这种情况下。所以我使用你建议的这个解决方案,你得到了我的 +1,但我接受了@Serghey Bishyr,因为他是第一个提出这个答案的人。
  • @Sunflame 不用担心,没关系。我刚刚在写我的长答案时看到了 Bishyr 的答案,但我决定不删除它,因为推拉模式在喋喋不休......
  • @maaartinus 在 Java 9 中 Optional 一直是 extended to support more operations。你应该使用最适合你的那个。
  • @maaartinus 这取决于你想做什么,甚至取决于个人品味。我个人不喜欢断言,我认为如果没有达到预期,我不应该总是抛出异常。如果这是一个测试,那么是的,我应该让测试失败,但是编程业务逻辑不像创建测试。有时抛出异常是可以的,有时什么也不做,或者返回一个默认值,甚至做其他事情也可以。我的意思是只使用Optional 而不是获取它的值。
  • @maaartinus 现在我明白你对ifPresent 的看法了。 Optional 更适合 Java 9 中的任务,即它具有 ifPresentOrElseor 允许链接 Optionals 等
【解决方案3】:

首先你不会得到NPE,而是NoSuchElementException。其次,可能会确定;但其他人可能会出现并没有意识到它不会抛出异常。

对于沙盒项目 - 是的,您不会关心并且可以忽略警告;对于生产代码,我不会禁用它(即使你可以)。

最后一点是,如果你这么确定,为什么不抛出异常呢?

orElseThrow(IAmSureThisWillNotHappenException::new)

【讨论】:

  • 你说得对,我不想禁用警告,我只是认为 IntelliJ 足够聪明,知道什么时候可以得到NoSuchElementException。如果结果总是在那里,那么我永远不会得到异常
  • @Sunflame 很可能需要提前编译器......目前没有这样的东西可用
  • 你也可以在这里抛出AssertionError
【解决方案4】:

您可以毫无问题地流式传输一个空列表,但如果您尝试获取空列表中的第一个元素,您将收到 NoSuchElementException

Stream API 清楚这一点,因此它们为您提供了多种处理方式:

Option1orElse 如果没有找到第一个元素,您可以返回“默认”值

String firstString = myList.stream().findFirst().orElse("Ups!");

Option2orElseGet 你可以使用Supplier&lt;String&gt;,如果没有找到第一个元素,则返回一个字符串

firstString = myList.stream().findFirst().orElseGet(mySupplier);

Option3orElseThrow 如果没有找到第一个元素,则可以抛出异常

firstString = myList.stream().findFirst().orElseThrow(WhatTerribleFailException::new);

System.out.println(fisrstString);

【讨论】:

    【解决方案5】:

    如果你知道你的Optional永远不会是空的,你可以使用@SuppressWarnings注解如下:

    @SuppressWarnings("ConstantConditions") String foo = Optional.of("bar").get();
    

    有时Optional.get 会引发NullPointerException,例如:

    Optional<String> it = Optional.empty();
    String foo = it.get();
              //   ^--- throws NullPointerException when this method is invoked
    

    SO 当使用此表达式时,Intellij 将报告 警告 检查。

    如果您想禁用所有合同检查,您可以执行以下操作:设置 -> 检查 -> 取消选中恒定条件和例外选项 -> 不要忘记点击底部的应用按钮来保存您的设置。

    如果您不想禁用除Optional.get() 警告之外的所有合同检查,您可以执行以下操作:设置 -> 检查 -> 勾选Constant condition & exceptions选项 -> 在右下角有一个框架可以配置Optional.get()warnings -> 不要忘记点击Apply底部的按钮保存您的设置。

    【讨论】:

    • 是的,我知道Optional 是如何工作的,所以我知道在这种情况下我可以获得 NPE,但在我的情况下,我不会得到任何异常。 (对我来说,@SuppressWarnings 不工作,但我不想使用这样不必要的代码)
    • @Sunflame 嗨,如果您想禁用此功能,您可以在脚下看到我编辑的答案。
    • 它没有帮助,警告仍然存在,但我不确定我是否为此禁用任何警告,那么当我不确定 .get()的结果是否为空。
    • @Sunflame 嗨,如果您不想禁用所有合同检查,可以在右下框中选择。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多