【问题标题】:Way to parseInt without try-catch in Java 8?在 Java 8 中没有 try-catch 的 parseInt 方法?
【发布时间】:2017-09-15 13:57:11
【问题描述】:

有没有更好的方法来尝试将一个可以是或不是整数的字符串转换为 int? Integer.parseInt(String value) 适用于“25”或“019”,但不适用于“hello”或“8A”。 在 Java 8 中,我们有可选值,例如:

public static void main(String[] args) {
    Optional<Integer> optionalResult = functionThatReturnsOptionalInteger();
    Integer finalValue = optionalResult.orElse(0);
    System.out.println(finalValue);
}

public static Optional<Integer> functionThatReturnsOptionalInteger() {
    Integer[] ints = new Integer[0];
    return Stream.of(ints).findAny();
}

您不需要检查空值,因为Optional 包装器公开了处理这种情况的有用方法。

但是如果你想parseInt一个字符串,那可以为null,或者不包含有效的整数,解决方法和往常一样:

public static Integer parseIntOrDefault(String toParse, int defaultValue) {
    try {
        return Integer.parseInt(toParse);
    } catch (NumberFormatException e) {
        return defaultValue;
    }
}

如何使用 Java 8 功能改进这一点,为什么 Integer.parseInt() 没有被重载return an Optional 以防参数错误? (或者只是将一个新方法 Integer.parseIntOptional() 添加到 Integer 包装器)

【问题讨论】:

  • 那将不允许旧代码在新版本上编译。
  • “覆盖”到底是什么意思?你的意思是替换,还是只是重载(在现有的 parseInt 方法上添加了一个 Optional-variant (我知道重载在这里不起作用))
  • @Felk,是的,抱歉,超载了!
  • 我发现 Integer.parseInt("hello") 抛出 NumberFormatException 比返回 Optional.empty() 更直观
  • 好的。附带说明:重载在这里不起作用,因为您不能仅通过具有不同的返回类型来重载。添加说Optional-variant 将需要不同的方法名称。虽然不会改变核心问题

标签: java java-8


【解决方案1】:

与其他答案现在已删除不同,我认为这与 Java 向后兼容并不真正有关。

因为一个空的Optional 代表一个不存在的值,这意味着该方法实际上有效,但没有返回任何结果。

但是,将hello 解析为整数将不起作用并且必须抛出异常,因为这是一个错误,而不是一个空结果。请记住,NumberFormatException 扩展了 IllegalArgumentException

更一般地说,Optional 用于处理可能不存在的值(而不是使用null),而不是用于错误处理。此外,Optional 没有提供任何方法来了解错误是什么以及为什么会出现错误。

【讨论】:

  • 我完全同意在出现错误时不要滥用Optional
  • 我同意Optional 的语义不适合用例,但我也看到了使用Optional 类的实际优势。我认为类似于 Result&lt;T&gt; 类表示返回值或错误,类似于 Rust 处理它的方式,在具有合适语义的同时具有相同的优势。
  • 我认为Optional 本身并不是为了处理可能的null 值,而是为了取代使用空值来表示可能不存在的值的做法。
  • @Felk "[...] 表达可能不存在的值":你说得对,我已经编辑了答案。
【解决方案2】:

在标准库 afaik 中不存在类似的东西,但您可以编写一个将 String 解析为 Optional&lt;Integer&gt; 的方法,如下所示:

public static Optional<Integer> parseInt(String toParse) {
    try {
        return Optional.of(Integer.parseInt(toParse));
    } catch (NumberFormatException e) {
        return Optional.empty();
    }
}

【讨论】:

  • 乔纳森的回答实际上包含了一个正当理由,说明为什么标准库中可能不存在这样的方法。
【解决方案3】:

Google 的 Guava 库提供了一个辅助方法来执行此操作:Ints.tryParse(String)。它在字符串不可解析时运行 null。您可以查看documentation

【讨论】:

    【解决方案4】:

    没有 try-catch 并且仍然返回 Optional - 你可以这样做

    Optional<Integer> result = Optional.ofNullable(input)
        .filter(str -> str.matches("-?\\d+"))
        .map(Integer::parseInt);
    

    编辑:更新正则表达式以支持负数

    警告:正如 cmets 中所指出的,如果解析的字符串超出 Integer.MIN_VALUEInteger.MAX_VALUE 的范围,仍会抛出 RuntimeException

    【讨论】:

    • 这将拒绝实际正确的负数,同时如果数值超出范围仍然可能抛出异常......
    • 您说得对,先生 - 我编辑了正则表达式以接受负数。至于范围,这取决于使用场景 - 我们很少有非常大的数字 - 由询问者决定他是否可以接受超出范围输入的 RuntimeException
    • @ViswanathLekshmanan 虽然您有自己的意见,但我现在更喜欢这种风格,尤其是自从 Java 8 出现以来。尝试阅读函数式编程结构,并避免返回空值。它鼓励更好的软件工程。
    【解决方案5】:

    我不想推测为什么这种方法不存在,但如果你都不喜欢,执行预测试或捕获异常,你需要重新实现,例如

    public static OptionalInt parseInt(String s) {
        if(s.isEmpty()) return OptionalInt.empty();
        final int len = s.length(), limit;
        final boolean negative;
        int i = 0;
        switch(s.charAt(0)) {
            case '-':
                i=1;
                if(len==1) return OptionalInt.empty();
                negative = true;
                limit = Integer.MIN_VALUE;
                break;
            case '+':
                i=1;
                if(len==1) return OptionalInt.empty();
                // fall-through
            default:
                negative = false;
                limit = -Integer.MAX_VALUE;
        }
        final int limitBeforeMul = limit / 10;
        int result = 0;
        for(; i < len; i++) {
            int digit = Character.digit(s.charAt(i), 10);
            if(digit < 0 || result < limitBeforeMul || (result *= 10) < limit + digit)
                return OptionalInt.empty();
            result -= digit;
        }
        return OptionalInt.of(negative? result: -result);
    }
    

    这与Integer.parseInt 基本相同,但是对于无效字符串返回一个空的OptionalInt 而不是抛出异常……

    您可能会注意到,最困难的部分是处理接近Integer.MIN_VALUE 的数字。 Integer.MAX_VALUE 正确。

    【讨论】:

      【解决方案6】:

      使用Mutiny可以这样写。

      如果你只想对结果做一些事情,你可以用一个表达式来做

      Uni.createFrom()
        .item(() -> Integer.parseInt(toParse))
        .onFailure().recoverWithItem(defaultValue)
        .subscribe().with(i -> System.out.println(i));
      

      或者创建一个像预期的方法

          public static Integer parseIntOrDefault(String toParse, int defaultValue) {
              Integer[] toReturn = new Integer[]{null};
              Uni.createFrom()
                 .item(() -> Integer.parseInt(toParse))
                 .onFailure().recoverWithItem(defaultValue)
                 .subscribe().with(i -> toReturn[0] = i);
              return toReturn[0];
      }
      

      此解决方案添加了一个库,但该库并非专门用于处理此问题。它使用反应式编程、futures、回调和流畅的 API,并被证明足够灵活来解决这个问题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-06-01
        • 1970-01-01
        • 2020-03-18
        • 1970-01-01
        • 2012-05-27
        • 1970-01-01
        • 2015-02-01
        • 1970-01-01
        相关资源
        最近更新 更多