Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Variable is already defined in method lambda

Consider the following almost compilable Java 8 code:

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:

variable user is already defined in method main(String[])

My question is: Why do Lambda expressions consider the variable that is being initialized on 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 being defined considered already defined?

like image 922
ryvantage Avatar asked Mar 31 '14 21:03

ryvantage


People also ask

Can we declare variable in lambda expression?

A lambda expression can't define any new scope as an anonymous inner class does, so we can't declare a local variable with the same which is already declared in the enclosing scope of a lambda expression. Inside lambda expression, we can't assign any value to some local variable declared outside the lambda expression.

Can a lambda expression shadow a variable that existed in the enclosing scope?

Lambda parameters are not allowed to shadow variables in the enclosing scopes.

Can we replace lambda expression with method reference?

To summarize: If the purpose of the lambda expression is solely to pass a parameter to an instance method, then you may replace it with a method reference on the instance. If the pass-through is to a static method, then you may replace it with a method reference on the class.

Can we use Non final variable in lambda?

A non-final local variable or method parameter whose value is never changed after initialization is known as effectively final. It's very useful in the context of the lambda expression. If you remember, prior to Java 8, we cannot use a non-final local variable in an anonymous class.


1 Answers

Let's go to the Java Language Specification on names and their scopes

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.

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.

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

within a class whose declaration is within the scope of v.

so a compile time error occurs. (It's declared in a lambda expression, not in a class.)

like image 59
Sotirios Delimanolis Avatar answered Nov 15 '22 23:11

Sotirios Delimanolis