Java 变量已在方法 lambda 中定义
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/22773003/
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
Variable is already defined in method lambda
提问by ryvantage
Consider the following almostcompilable Java 8 code:
考虑以下几乎可编译的 Java 8 代码:
public static void main(String[] args) {
LinkedList<User> users = null;
users.add(new User(1, "User1"));
users.add(new User(2, "User2"));
users.add(new User(3, "User3"));
User user = users.stream().filter((user) -> user.getId() == 1).findAny().get();
}
static class User {
int id;
String username;
public User() {
}
public User(int id, String username) {
this.id = id;
this.username = username;
}
public void setUsername(String username) {
this.username = username;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public int getId() {
return id;
}
}
You'll notice User user = users.stream().filter((user) -> user.getId() == 1).findAny().get();
throws a compiler error:
你会注意到User user = users.stream().filter((user) -> user.getId() == 1).findAny().get();
抛出一个编译器错误:
variable user is already defined in method main(String[])
变量 user 已在方法 main(String[]) 中定义
My question is: Why do Lambda expressions consider the variable that is being initializedon the same line as the Lambda expression as already defined? I understand Lambdas look outside themselves for (and use) local variables, so you can't name the variables you use inside the Lambda the same as an outside variable. But why is the variable that is beingdefined considered alreadydefined?
我的问题是:为什么 Lambda 表达式会考虑在与 Lambda 表达式已定义的同一行上初始化的变量?我了解 Lambda 会在自身外部寻找(并使用)局部变量,因此您不能将在 Lambda 内部使用的变量命名为与外部变量相同的名称。但是为什么正在定义的变量被认为已经定义了?
采纳答案by Sotirios Delimanolis
Let's go to the Java Language Specification on names and their scopes
让我们转到有关名称及其范围的 Java 语言规范
The scope of a formal parameter of a method (§8.4.1), constructor (§8.8.1), or lambda expression (§15.27) is the entire body of the method, constructor, or lambda expression.
The scope of a local variable declaration in a block (§14.4) is the rest of the block in which the declaration appears, starting with its own initializer and including any further declarators to the right in the local variable declaration statement.
方法(第 8.4.1 节)、构造函数(第 8.8.1 节)或 lambda 表达式(第 15.27 节)的形参的范围是方法、构造函数或 lambda 表达式的整个主体。
块中局部变量声明的范围(第 14.4 节)是该声明出现的块的其余部分,从它自己的初始值设定项开始,并包括局部变量声明语句右侧的任何其他声明符。
Then, on the subject of shadowing and obscuring
然后,关于阴影和模糊的主题
A local variable (§14.4), formal parameter (§8.4.1, §15.27.1), exception parameter (§14.20), and local class (§14.3) can only be referred to using a simple name, not a qualified name (§6.2).
Some declarations are not permitted within the scope of a local variable, formal parameter, exception parameter, or local class declaration because it would be impossible to distinguish between the declared entities using only simple names.
It is a compile-time error if the name of a local variable v is used to declare a new variable within the scope of v, unless the new variable is declared within a class whose declaration is within the scope of v.
局部变量(第 14.4 节)、形式参数(第 8.4.1 节、第 15.27.1 节)、异常参数(第 14.20 节)和局部类(第 14.3 节)只能使用简单名称而不是限定名称来引用(第 6.2 节)。
某些声明在局部变量、形式参数、异常参数或局部类声明的范围内是不允许的,因为仅使用简单名称无法区分声明的实体。
如果使用局部变量 v 的名称来声明 v 范围内的新变量,则会出现编译时错误,除非新变量是在其声明在 v 范围内的类中声明的。
So, in
所以,在
User user = users.stream().filter((user) -> user.getId() == 1).findAny().get();
, the scope of the variable user
is everything after it in that block. Now you are trying to use the name of that variable to declare a new variable within the scope, but not
,变量的范围user
是该块中它之后的所有内容。现在您正在尝试使用该变量的名称在范围内声明一个新变量,但不是
within a class whose declaration is within the scope of v.
在声明在 v 范围内的类中。
so a compile time error occurs. (It's declared in a lambda expression, not in a class.)
所以会出现编译时错误。(它是在 lambda 表达式中声明的,而不是在类中。)
回答by Mzf
look at the code
看代码
User user = users.stream().filter((user) -> user.getId() == 1).findAny().get();
The variable name is user
and the variable inside the lambda is also user
变量名是user
,lambda 内部的变量也是user
try changing it to be something like this
尝试将其更改为这样的
User user = users.stream().filter((otherUser) -> otherUser.getId() == 1).findAny().get();
回答by Ingo
It is the same as with any other local variables: you're not allowed to shadow them in more inner {} blocks.
它与任何其他局部变量相同:不允许在更多内部 {} 块中隐藏它们。
回答by Thirumalai Parthasarathi
The question is pretty old, but i thought my answer can add better clarity to the already given answers. Particularly to that of @Sotirios Delimanolis . The lambda assignment in
这个问题已经很老了,但我认为我的答案可以使已经给出的答案更加清晰。特别是@Sotirios Delimanolis 的那个。中的 lambda 赋值
User user = users.stream().filter((user) -> user.getId() == 1).findAny().get();
fails for the same reason the following code fails.
失败的原因与以下代码失败的原因相同。
Object e = null;
try{
throw new Exception();
} catch(Exception e) { // compilation fails because of duplicate declaration
//do nothing
}
A local variable (§14.4), formal parameter (§8.4.1, §15.27.1), exception parameter (§14.20), and local class (§14.3) can only be referred to using a simple name, not a qualified name (§6.2).
Some declarations are not permitted within the scope of a local variable, formal parameter, exception parameter, or local class declaration because it would be impossible to distinguish between the declared entities using only simple names.
局部变量(第 14.4 节)、形式参数(第 8.4.1 节、第 15.27.1 节)、异常参数(第 14.20 节)和局部类(第 14.3 节)只能使用简单名称而不是限定名称来引用(第 6.2 节)。
某些声明在局部变量、形式参数、异常参数或局部类声明的范围内是不允许的,因为仅使用简单名称无法区分声明的实体。
Because lambdas have the same scope of all the things mentioned above, this fails.
因为 lambdas 具有上述所有内容的相同范围,所以这失败了。
回答by ZhekaKozlov
Note, this limitation is going to be removed in the future releases. Quote from JEP-302:
请注意,此限制将在未来版本中删除。引自JEP-302:
Lambda parameters are not allowed to shadow variables in the enclosing scopes. (In other words, a lambda behaves like a for statement - see JLS) This often causes problems, as in the following (very common) case:
不允许 Lambda 参数隐藏封闭范围内的变量。(换句话说,lambda 的行为类似于 for 语句 - 请参阅 JLS)这通常会导致问题,如下面(非常常见)的情况:
Map<String, Integer> msi = ...
...
String key = computeSomeKey();
msi.computeIfAbsent(key, key -> key.length()) //error
Here, the attempt to reuse the name key as a lambda parameter in the computeIfAbsent call fails, as a variable with the same name was already defined in the enclosing context.
It would be desirable to lift this restriction, and allow lambda parameters (and locals declared with a lambda) to shadow variables defined in enclosing scopes. (One possible argument against is readability: if lambda parameters are allowed to shadow, then in the above example, the identifier 'key' means two different things in the two places where it is used, and there seem to be no syntactic barrier to separate the two usages.)
在这里,尝试在 computeIfAbsent 调用中重用名称键作为 lambda 参数失败,因为在封闭上下文中已经定义了具有相同名称的变量。
最好取消此限制,并允许 lambda 参数(以及使用 lambda 声明的局部变量)隐藏在封闭作用域中定义的变量。(反对的一个可能的论点是可读性:如果允许 lambda 参数隐藏,那么在上面的例子中,标识符“key”在使用它的两个地方意味着两个不同的东西,似乎没有语法障碍来分隔两种用法。)