变量已在方法 lambda 中定义

2022-09-02 20:48:19

请考虑以下几乎可编译的 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;
    }
}

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

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

我的问题是:为什么 Lambda 表达式会考虑与 Lambda 表达式在同一行上初始化的变量?我了解 Lambda 会在自身外部查找(并使用)局部变量,因此您不能将 Lambda 内部使用的变量命名为与外部变量相同的名称。但是,为什么正在定义的变量被认为是已经定义的呢?


答案 1

让我们转到有关名称及其作用域的 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();

,则变量的作用域是该块中它之后的所有内容。现在,您正在尝试使用该变量的名称在作用域内声明新变量,但不是user

在声明在 v 的范围内的类中。

因此发生编译时错误。(它是在 lambda 表达式中声明的,而不是在类中声明的。


答案 2

查看代码

User user = users.stream().filter((user) -> user.getId() == 1).findAny().get();

变量名称是,lambda 内的变量也是useruser

尝试将其更改为类似的东西

User user = users.stream().filter((otherUser) -> otherUser.getId() == 1).findAny().get();

推荐