Set final variable inside a lambda gives error, but if it is not final, gives another error

Asked

Viewed 104 times

1

public UserEntity findByEmail(String email) {
    final UserEntity result;
    getDatabase().forEach((Long id, UserEntity user) -> {
        boolean isName = user.getEmail().equals(email);
        if (isName) {
            result = user;
        }
    });

    return result;
}

I wanted to understand why this method does not compile. When I withdraw the final of result, he says:

Variable used in lambda Expression should be final or effectively final

When I put final:

Cannot assign a value to final variable 'result'

This is my getDatabase(). He’s just one Map.

protected Map<Long, E> getDatabase() {
    return database;
}
  • 2

    You need to put the ending in isName, which is the variable inside the foreach, not the result.

  • A variable declared outside the lambda (in this case, outside the forEach) can only be used within it if it’s final. But a variable final can only have a set value only once, but within that loop is the possibility of it being set more than once. So the solution is to refactor your code and not depend on setting the variable within the loop - using a for normal, or, if getDatabase() for a stream, use filter and findFirst, for example.

  • getDatabase() does not have stream method...

  • So please click on [Edit] and inform what is getDatabase(). Try to see if he has another method to travel it, for example

  • Edited. The database is a map.

  • To walk a Map, use a for normal, so it doesn’t have this problem of final: https://stackoverflow.com/q/46898

Show 1 more comment

2 answers

2

The use of the reserved word final in java restricts that that variable will receive a value in the definition and can never be changed again. Since you did not give any initial value, the variable will be null forever.

The use of foreach to filter the data you want is also not recommended for this type of operation. If you need the first correct email found, you can use the filter.

User retorno = getDatabase()
                          .entrySet()
                          .stream()
                          .filter(user -> user.getEmail.equals(email))
                          .findFirst()
                          .get();

In addition to these considerations, if you are picking up this list of users from a database, you could already search only the correct record directly in the query.

  • getDatabase() does not have stream method...

2

The variable result was declared outside the lambda (in this case, outside the forEach), then it can only be used within it if it’s final.

But a variable final may only have an assigned value only once. Only inside your own loop there is the possibility of it being set more than once (if you have more than one user with the email indicated, for example, you will enter twice in the if, setting the variable final twice).

That is, if you use final, One mistake, but if you don’t use it, another. So the solution is to refactor your code and not depend on setting the variable within the forEach. Like getDatabase() returns a Map, just walk you through it with a for normal:

public UserEntity findByEmail(String email) {
    for (Map.Entry<Long, UserEntity> entry : getDatabase().entrySet()) {
        if (entry.getValue().getEmail().equals(email)) {
            // encontrou o email, já retorna o usuário
            return entry.getValue();
        }
    }

    // não encontrou nada, retorna null (ou pode lançar uma exceção)
    return null;
}

The method entrySet returns a set of Map.Entry, from which it is possible to access the key and the respective value of the Map. Using getValue(), I get the UserEntity, and then just do the checks you need.


But since you are only checking the values and ignoring the keys, you can only use the method values(), that already returns directly all the UserEntity:

public UserEntity findByEmail(String email) {
    for (UserEntity user : getDatabase().values()) {
        if (user.getEmail().equals(email)) {
            // encontrou o email, já retorna o usuário
            return user;
        }
    }

    // não encontrou nada, retorna null (ou pode lançar uma exceção)
    return null;
}

Browser other questions tagged

You are not signed in. Login or sign up in order to post.