Java 8 catch 22 with lambda 表达式并且有效地最终

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22777915/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 17:49:04  来源:igfitidea点击:

Java 8 catch 22 with lambda expressions and effectively final

javajava-8

提问by Josh Stone

I'm playing with Java 8 and hit a basic scenario that illustrates a catch 22 where fixing one compile error causes another compile error. The scenario (which is just an example simplified from something more complex):

我正在使用 Java 8 并遇到一个基本场景,该场景说明了 catch 22,其中修复一个编译错误会导致另一个编译错误。场景(这只是从更复杂的事情简化的一个例子):

public static List<String> catch22(List<String> input) {
    List<String> result = null;
    if (input != null) {
      result = new ArrayList<>(input.size());
      input.forEach(e -> result.add(e)); // compile error here
    }

    return result;
}

I get a compile error:

我收到一个编译错误:

Local variable result defined in an enclosing scope must be final or effectively final

在封闭作用域中定义的局部变量结果必须是最终的或有效的最终结果

If I change the first line to:

如果我将第一行更改为:

List<String> result;

I get a compile error on the last line:

我在最后一行收到编译错误:

The local variable result may not have been initialized

局部变量结果可能尚未初始化

It seems like the only approach here is to pre-initialize my result to an ArrayList, which I don't want to do, or not use lambda expressions. Am I missing any other solution?

似乎这里唯一的方法是将我的结果预先初始化为 ArrayList,我不想这样做,或者不使用 lambda 表达式。我错过了任何其他解决方案吗?

采纳答案by Rohit Jain

The error is coming because your resultlist is not effectively finalwhich is a requirement for it's usage in lambda. One option is to declare the variable inside the ifcondition, and return null;outside. But I don't think that would be good idea. Your current method is not doing anything productive. It would make much more sense to return an empty list from it.

错误即将到来,因为您的result列表无效final,这是在 lambda 中使用它的要求。一种选择是在if条件内部和return null;外部声明变量。但我认为这不是个好主意。您当前的方法没有做任何富有成效的事情。从中返回一个空列表会更有意义。

Having said that all, I would say, since you are playing with Java 8, use Optionalalong with streams here:

说了这么多,我想说,既然您正在使用 Java 8,请Optional在此处与流一起使用:

public static List<String> catch22(List<String> input) {
    return Optional.ofNullable(input)
            .orElse(new ArrayList<String>())
            .stream().collect(Collectors.toList());
}

And if you want to return null, I'll probably change your method to:

如果您想返回null,我可能会将您的方法更改为:

public static List<String> catch22(List<String> input) {
    if (input == null) return null;
    return input.stream().collect(Collectors.toList());
    // Or this. B'coz this is really what your code is doing.
    return new ArrayList<>(input);
}

回答by Raul Guiu

Push the declaration inside the block with input != null. Example:

使用 input != null 将声明推入块内。例子:

public static List<String> catch22(List<String> input) {
    if (input != null) {
        List<String> result;
        result = new ArrayList<>(input.size());
        input.forEach(e -> result.add(e)); // compile error here
        return result;
    } else {
        return null; // or empty list or something that suits your app
    }
}

回答by Arnoud

By making the result final and putting the null assignment in the else block you can keep the current structure of your method and 'solve' your catch22.

通过使结果最终并将空赋值放在 else 块中,您可以保持方法的当前结构并“解决”您的 catch22。

public static List<String> catch22(List<String> input) {
    final List<String> result;
    if (input != null) {
        result = new ArrayList<>(input.size());
        input.forEach(e -> result.add(e));
    } else {
        result = null;
    }

    return result;
}

回答by Edwin Buck

forEach(...)applies an operation on each element of the Stream. You really don't want that, you want a Stream"consumer" which produces a single output of List<String>.

forEach(...)对 的每个元素应用操作Stream。你真的不想要那个,你想要一个Stream“消费者”,它产生一个单一的输出List<String>.

Fortunately these are considered Collectors in the current framework, and Collectors.toList()does exactly what you want.

幸运的是,这些Collector在当前框架中被认为是s,并且Collectors.toList()完全符合您的要求。

List<String> duplicate = input.stream().collect(Collectors.toList());

回答by Carlos Bribiescas

You can do this

你可以这样做

public static List<String> catch22(List<String> input) {
List<String> result = null;
if (input != null) {
  result = new ArrayList<>(input.size());
  List<String> effectivelyFinalResult = result;
  input.forEach(e -> effectivelyFinalResult.add(e)); // No compile error here
}

return result;
}

to get around it.

绕过它。

回答by Kumar Abhishek

Its presence prevents the introduction of a new class of multithreading bugs involving local variables.

它的存在可以防止引入涉及局部变量的新类多线程错误。

Local variables in Java have until now been immune to race conditions and visibility problems because they are accessible only to the thread executing the method in which they are declared. But a lambda can be passed from the thread that created it to a different thread, and that immunity would therefore be lost if the lambda, evaluated by the second thread, were given the ability to mutate local variables.

到目前为止,Java 中的局部变量不受竞争条件和可见性问题的影响,因为它们只能被执行声明它们的方法的线程访问。但是一个 lambda 可以从创建它的线程传递到另一个线程,因此如果由第二个线程评估的 lambda 被赋予改变局部变量的能力,那么这种免疫力就会丧失。

Even the ability to read the value of mutable local variables from a different thread would introduce the necessity for synchronization or the use of volatile in order to avoid reading stale data.

即使能够从不同的线程读取可变局部变量的值也会引入同步的必要性或使用 volatile 以避免读取过时的数据。

回答by Mr.Q

Here, i summarized some general solutions for lambdas expressions modifying method local variables.

在这里,我总结了 lambdas 表达式修改方法局部变量的一些通用解决方案。

Lambdas Expressions are actually anonymous inner classes in a concise form. When we use them in local methods we should take several constraints into consideration:

Lambdas 表达式实际上是一种简洁形式的匿名内部类。当我们在本地方法中使用它们时,我们应该考虑几个约束:

  • lambdas expressions(anonymous inner class) cant modify local variables of surrounding method link herethey must be final or in java 8 effectively final

  • Unlike instance variables, local variables do not get default values and if you want to use or return them you must initialize them first

  • lambdas 表达式(匿名内部类)不能在这里修改周围方法链接的局部变量,它们必须是最终的或在 Java 8 中 有效地最终

  • 与实例变量不同,局部变量没有默认值,如果你想使用或返回它们,你必须先初始化它们

When these two constraints collide (in case of defining a lambdas in a method where lambdas modifies local variable in surrounding method), we get into trouble

当这两个约束发生冲突时(如果在方法中定义一个 lambdas,其中 lambdas 修改了周围方法中的局部变量),我们就会遇到麻烦

solutions:

解决方案:

solution 1: do not modify method local variable in lambdas or use instance variables instead

解决方案 1:不要修改 lambdas 中的方法局部变量或使用实例变量代替

solution 2: do some tricks, for example copy local variable into another one and pass that to lambdas:

解决方案 2:做一些技巧,例如将局部变量复制到另一个变量中并将其传递给 lambdas:

public static List<String> catch22(List<String> input) {
 List<String> result = null;
 if (input != null) {
   result = new ArrayList<>(input.size());
   List<String> effectivelyFinalResult = result;
   input.forEach(e -> effectivelyFinalResult.add(e)); 
 }
 return result;
}

or limit local variables scope so you dont get into trouble for not initializing them:

或者限制局部变量范围,这样你就不会因为没有初始化它们而遇到麻烦:

public static List<String> catch22(List<String> input) {
if (input != null) {
    List<String> result; // result gets its value in the lambdas so it is effectively final
    result = new ArrayList<>(input.size());
    input.forEach(e -> result.add(e));
    return result;
} else {
    return null; 
}
}