为什么Java 8 Stream forEach方法的行为有所不同? [英] Why Java 8 Stream forEach method behaves differently?

查看:206
本文介绍了为什么Java 8 Stream forEach方法的行为有所不同?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

根据我对java 8 lambda表达式的理解,如果我们没有在大括号中的 - >之后包含代码,那么将隐式地返回值。但是在下面的例子中, forEach 方法期望 Consumer 并且表达式返回值,但编译器没有提供错误Eclipse。

  List< StringBuilder> messages = Arrays.asList(new StringBuilder(),new StringBuilder()); 
$ b $ messages.stream()。forEach(s-> s.append(helloworld)); //正常工作

messages.stream()。forEach (StringBuilder s) - > s.append(helloworld)); //正常工作

messages.stream()。forEach(s-> s); //不工作,Void方法不能返回值

messages.stream()。forEach(s-> s.toString()); //正常工作

messages.stream()。forEach(s-> {return s.append(helloworld);}); //不工作,Void方法不能返回值

messages.stream()。forEach((StringBuilder s) - > {return s.append(helloworld);}); //无效,Void方法无法返回值

s。 append 返回 StringBuilder s.toString()返回字符串但lambda将它视为 void



我在这里错过了什么?为什么编译器在我们调用对象的方法时不给出错误? 解决方案

JLS 15.27.3。 Lambda表达式的类型:b
$ b


如果所有的
跟随在一个lambda表达式中,则与函数类型一致是真的:



  • 函数类型没有类型参数。

  • >

    lambda表达式的数量与函数类型的参数类型数量相同。

  • 如果lambda表达式是明确类型的,形式参数类型与函数类型的参数类型相同。




    • 如果假设lambda参数具有相同的类型作为函数类型的参数类型,那么:

    • )或者一个与void无关的块。如果函数类型的结果是一个(非空类型)类型R,那么i) lambda body是一个与R
      兼容的表达式或者ii)lambda体是一个值兼容的
      块,并且每个结果表达式(§15.27.2)与
      中的R都兼容赋值上下文。




上面突出显示的语句表示任何语句lambda表达式(即一个没有块的lambda表达式)匹配一个函数接口,其单个方法的返回类型是 void (例如 Consumer forEach 方法所需的接口)。



这解释了为什么 s.append(helloworld)& s.toString()(你的1,2和4个例子)可以很好地用作语句lambda表达式。



例5& 6不起作用,因为它们具有与值兼容的lambda表达式的块lambda体。要成为 void-compatible ,所有的返回语句都不会返回任何内容(即 return; )。



另一方面,下面的 void-compatible 块lambda体将会通过编译:

  messages.stream()。forEach(s-> {s.append(helloworld);}); 
messages.stream()。forEach(s-> {s.append(helloworld); return;});

第四个例子 - messages.stream()。forEach(s-> ; s); 由于相同的原因不起作用,下面的方法不通过编译:

  void方法(StringBuilder s)
{
s;
}


As per my understanding of java 8 lambda expressions, if we don't include code after "->" in curly braces that value will be returned implicitly. But in case of below example, forEach method expects Consumer and expression returns value but the compiler is not giving an error in Eclipse.

List<StringBuilder> messages = Arrays.asList(new StringBuilder(), new StringBuilder());

messages.stream().forEach(s-> s.append("helloworld"));//works fine 

messages.stream().forEach((StringBuilder s)-> s.append("helloworld")); //works fine 

messages.stream().forEach(s-> s); // doesn't work , Void methods cannot return a value

messages.stream().forEach(s-> s.toString()); // works fine

messages.stream().forEach(s-> {return s.append("helloworld");}); // doesn't work , Void methods cannot return a value

messages.stream().forEach((StringBuilder s)-> {return s.append("helloworld");});  // doesn't work , Void methods cannot return a value

s.append returns StringBuilder and s.toString() returns String but lambda treats it as void.

What am I missing here? Why isn't the compiler giving an error when we invoke method on object?

解决方案

From JLS 15.27.3. Type of a Lambda Expression:

A lambda expression is congruent with a function type if all of the following are true:

  • The function type has no type parameters.

  • The number of lambda parameters is the same as the number of parameter types of the function type.

  • If the lambda expression is explicitly typed, its formal parameter types are the same as the parameter types of the function type.

    • If the lambda parameters are assumed to have the same types as the function type's parameter types, then:

    • If the function type's result is void, the lambda body is either a statement expression (§14.8) or a void-compatible block.

    • If the function type's result is a (non-void) type R, then either i) the lambda body is an expression that is compatible with R in an assignment context, or ii) the lambda body is a value-compatible block, and each result expression (§15.27.2) is compatible with R in an assignment context.

The highlighted sentence above means that any statement lambda expression (i.e. a lambda expression without a block) matches a functional interface whose single method's return type is void (such as the Consumer functional interface required by the forEach method).

This explains why s.append("helloworld") & s.toString() (your 1,2 & 4 examples) work fine as statement lambda expressions.

examples 5 & 6 don't work, since those have block lambda bodies which are value-compatible lambda expressions. To be void-compatible, all the return statements must return nothing (i.e. just return;).

On the other hand, the following void-compatible block lambda bodies will pass compilation :

messages.stream().forEach(s-> {s.append("helloworld");});
messages.stream().forEach(s-> {s.append("helloworld"); return;});

Your 4th example - messages.stream().forEach(s-> s); doesn't work for the same reason the following method doesn't pass compilation :

void method (StringBuilder s)
{
    s;
}

这篇关于为什么Java 8 Stream forEach方法的行为有所不同?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆