1

The Customer class uses constructor injection (recommended over field injection), but the instance field is not usable inside a lambda.

@Component
public class Customer {

  private final Account account;

  @Autowired
  public Customer(final Account account) {
    this.account = account;
  }

  // Works perfectly fine
  Callable propCallable = new Callable<String>(){
    @Override
    public String call() throws Exception {
        return account.accountId();
    }
  };

  //Shows warning : Variable account might not have been initialized
  Callable lambdaCallable = () -> {
    return account.accountId();
  };
}

I'm just curious to know if there is a better way to use instance variable inside the lambda, rather than anonymous class?

Note: I prefer to keep account as final.

Thanks in advance

MPhil
  • 881
  • 1
  • 12
  • 24
technoJ
  • 175
  • 1
  • 1
  • 16
  • 1
    If you want `lambdaCallable` to be an instance variable, initialize it in constructor just as account. Also, if its scope is within a method, that warning won't show up – noiaverbale Feb 22 '19 at 10:02
  • You can change the declaration to `Callable lambdaCallable = () -> Customer.this.account.accountId();`, but the better solution would be to assign the variable inside the constructor, after `account` has been assigned, as noiaverbale said, i.e. `public Customer(Account account) { this.account = account; lambdaCallable = () -> this.account.accountId(); }`. Note that the placement of the field declaration at the end of the class does not change the fact that field initializers are executed before the body of the constructor (except the `super()` call. – Holger Feb 22 '19 at 12:02

2 Answers2

2

There are some differences between an anonymous class and lambda. In this case the main one is:

Compilation – Anonymous compiles to a class, while lambda is an invokedynamic instruction

Now about the compiler error. By Java order of initialization rules, your lambda initialization happens before the 'account' assignment in the constructor. Not sure, but there is no such error with the anonymous class because of the compilation difference.

So, you can return the lambda from method, or move 'lambdaCallable' initialization to the constructor.

public class DemoApplication {

    public class Customer {

        private final Account account;

        public Customer(final Account account) {
            this.account = account;
        }

        // Works perfectly fine
        Callable propCallable = new Callable<String>(){
            @Override
            public String call() throws Exception {
                return account.accountId();
            }
        };

        Callable getCallable() {
            return account::getId;
        }

//        Callable lambdaCallable = () -> {
//            return account.accountId();
//        };
    }
}
ilinykhma
  • 980
  • 1
  • 8
  • 14
  • 1
    Small correction; the field initializer is not executed before the constructor is called, but right after the super constructor has been executed, which would be already inside the constructor when you make the call explicit. But the key point is correct, it happens before the `this.account = account;` assignment. Note that your `Callable` does not hold a reference to the `Customer` instance anymore. That might be even a desirable effect in most cases, but for some corner cases, it’s worth understanding the difference. – Holger Feb 22 '19 at 12:06
  • Thanks, for the impotant notes. I did the correction about 'account' assignment. – ilinykhma Feb 22 '19 at 12:41
1

Move lambdaCallable definition to inside the constructor:

private final Callable<Long> lambdaCallable;
@Autowired
public Customer(final Novel account) {
   this.account = account;
   lambdaCallable = account::getId;
}
Mạnh Quyết Nguyễn
  • 17,677
  • 1
  • 23
  • 51