【问题标题】:Initialize variable by evaluating lambda directly通过直接评估 lambda 来初始化变量
【发布时间】:2017-12-22 14:28:04
【问题描述】:

我想知道是否有一种方法可以直接在定义的同一表达式中对 lambda 函数调用 .apply().get()。当我想初始化一个可能是私有的变量时,我想到了这个问题,但我不能将它声明为 final,因为该值是可以引发异常的函数的返回值。例如,考虑Files.size(path)

 final s = Files.size(path);
 // code that uses s

现在,如果我想为s使用默认值,如果有异常,我必须添加一个try/catch,但这是一个语句而不是表达式:

s = 0;
try {
    s = Files.size();
}
catch (IOException e) {}

 // code that uses s

显而易见的解决方案是只声明一个包装 try/catch 的小帮助函数,例如getFileSizeOrZero。我还可以编写一个通用函数,将其包装为一般情况。这很好并且具有描述性,但是更局部的函数方法会更好。

在 C++ 中,我使用了 lambda:

const s = []() {
    try {
        return Files.size(path);
    } catch (...) {
        return 0;
    }
}();

所以我想知道这是否可以在 Java 中完成。我能想到的最好的方法是

final int s = new IntSupplier() {
    @Override
    public int getAsInt() {
        try {
            return Files.size(path);
        }
        catch (IOException e) {
            return 0;
        }
    }
 }.getAsInt();

这似乎很有表现力,所以我希望有一些我不知道启用的语法

final int s = () -> {
    try {
        return Files.size(path);
    }
    catch (IOException e) {
        return 0;
    }
}.apply();

看来问题是没有像C++那样的函数调用运算符,所以必须有一个可以调用的命名成员函数。

【问题讨论】:

  • " 我不能将它声明为 final,因为该值是可以引发异常的函数的返回值" 为什么这会阻止您将其设为 final?
  • @AndyTurner 也许我表述错误了一点。这并没有阻止我,但是我必须将变量放在 try 语句的范围内,所以我实际上必须将所有使用它的代码放入 try 语句中。
  • "显而易见的解决方案是只声明一个包装 try/catch 的小帮助函数,例如 getFileSizeOrZero。我还可以编写一个将其包装为一般情况的通用函数。这很好且具有描述性,但更局部的函数方法会更好。” - 为什么会更好?
  • 我不喜欢创建在外部范围内使用这个非常具体的本地原因的函数。它只会污染课堂。这不是很糟糕,因为它们是私有函数,但我只想保持本地化。
  • @AndyTurner 谢谢。我一直认为这是根据程序路径定义的。这是一种按块对路径的不直观近似。

标签: java lambda


【解决方案1】:

您可以这样做,但您需要将匿名 lambda 显式转换为正确的类型。那是因为你需要将lambda的类型强制到具体的函数接口,Java无法猜测最终的类型,例如:

final int x = ((IntSupplier)() -> 5).getAsInt();

如果你不喜欢它,你需要将它存储在一个变量中:

final IntSupplier xx = () -> 5;
final int x = xx.getAsInt();

【讨论】:

    【解决方案2】:

    你的一个问题是Files.size(path)实际上返回一个long,所以你需要将它分配给一个。

    你可以这样做:

        long size = ((LongSupplier) () -> {
            try
            {
                return Files.size(path);
            }
            catch (IOException e)
            {
                return 0;
            }
        }).getAsLong();
    

    所以你只需要转换 lambda 并有一个逻辑行表达式。

    我还要说一个通用的功能应该是有序的

    public long defaultOnException(LongSupplier s, long defaultValue) {
        try { 
            return s.getAsLong(); 
        } catch (Exception e) {
            return defaultValue;
        }
    }
    

    这将使您的客户端代码

    long size = defaultOnException(() -> Files.size(path), 0);
    

    【讨论】:

      【解决方案3】:

      我不能将它声明为final,因为该值是可以抛出异常的函数的返回值

      为什么这会阻止你完成它?只需在 try/catch 之外声明变量即可。

      final long s;
      {
        long ss;
        try {
          ss = Files.size(path);
        } catch (IOException e) {
          ss = 0;
        }
        s = ss;
      }
      

      请注意,在 try/catch 中,您不能简单地使用 s 代替 ss,因为有明确分配的规则,specifically

      如果满足以下所有条件,则在 catch 块之前肯定未分配 V:

      • 在 try 块之后,V 肯定是未分配的。
      • ...

      第一个条件不成立,所以其余的无关紧要。

      【讨论】:

        【解决方案4】:

        还有一个选择:

            final int s = (IntSupplier) () -> {
                try {
                    return Files.size(path);
                } catch (IOException e) {
                    return 0;
                }
            }.getAsInt();
        

        【讨论】:

          【解决方案5】:

          使用泛型的另一种选择:

              final long size = ((Supplier<Long>)(() -> {
                  try {
                      return Files.size(path);
                  } catch (IOException e) {
                      return 0L;
                  }
              })).get();
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2016-03-11
            • 2020-05-31
            • 1970-01-01
            • 2017-04-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多