变量已经在方法lambda中定义

ryv*_*age 16 java lambda java-8

考虑以下几乎可编译的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;
    }
}
Run Code Online (Sandbox Code Playgroud)

您会注意到User user = users.stream().filter((user) -> user.getId() == 1).findAny().get();抛出编译器错误:

变量用户已在方法main(String [])中定义

我的问题是:为什么Lambda表达式会考虑与已定义的Lambda表达式在同一行上初始化的变量?我理解Lambdas在外部查找(并使用)局部变量,因此您不能将Lambda中使用的变量命名为外部变量.但是,为什么是变量定义的考虑已经确定?

Sot*_*lis 14

让我们来看看关于名称及其范围的Java语言规范

方法(第8.4.1节),构造函数(第8.8.1节)或lambda表达式(第15.27节)的形式参数的范围是方法,构造函数或lambda表达式的整个主体.

块(第14.4节)中局部变量声明范围是声明出现的块的其余部分,从其自己的初始化器开始,并包括局部变量声明语句中右侧的任何其他声明.

然后,关于阴影和模糊的主题

局部变量(第14.4节),形式参数(第8.4.1节,第15.27.1节),异常参数(第14.20节)和本地类(第14.3节)只能使用简单名称引用,而不能使用限定名称引用(第6.2节).

在局部变量,形式参数,异常参数或本地类声明的范围内不允许某些声明,因为仅使用简单名称来区分声明的实体是不可能的.

如果使用局部变量v的名称来声明v的范围内的新变量,那么这是一个编译时错误,除非在声明在v的范围内的类中声明了新变量.

所以,在

User user = users.stream().filter((user) -> user.getId() == 1).findAny().get();
Run Code Online (Sandbox Code Playgroud)

,变量的范围user是该块之后的所有内容.现在,您尝试使用该变量的名称在范围内声明一个新变量,但不是

在声明属于v范围内的类中.

所以发生编译时错误.(它在lambda表达式中声明,而不是在类中声明.)

  • 这个答案基本上是说"因为JLS这么说" - 这没有错或什么,但它并没有真正解释为什么匿名类是免除的,闭包不是.决定是武断的吗?人们是否认为重复变量名称会导致闭包中的混淆太多而不会导致类中的混淆?在闭包中实现范围是否太难,但在匿名类中却没有? (10认同)
  • @MirroredFate有时很难从JLS中的文本中辨别出基本原理,但在这种情况下,它落在了匿名内部类(AIC)和lambda之间的区别.AIC是一个真正的**类,真正的类有自己的范围.此范围内的名称可以影响本地或封闭类的成员.相比之下,Lambdas没有自己的范围,因此禁止当地人互相遮蔽.就个人而言,我发现AIC的范围规则与其词汇环境相混淆,并且lambda规则更简单. (4认同)

Mzf*_*Mzf 7

看看代码

User user = users.stream().filter((user) -> user.getId() == 1).findAny().get();
Run Code Online (Sandbox Code Playgroud)

变量名是user,lambda中的变量也是user

尝试将其改为这样的东西

User user = users.stream().filter((otherUser) -> otherUser.getId() == 1).findAny().get();
Run Code Online (Sandbox Code Playgroud)

  • 嗯,我认为OP意识到了这一点 (7认同)
  • 是的,我认为问题是为什么lambda`user`变量不在闭包上下文中遮蔽`user`变量?否则这个问题很无聊. (2认同)