1

Perhaps the title of question isnt well created, but i couldnt understand what else I could put in there.

I wanted to understand, why the first one out of the two below cases works, and how can I get the second one to work as well, given the overall implementation is same.

From a code point of view, what principal etc is causing the first to work as is, i.e. broadening the scope of variables that can be used, so I can learn more about that.

I want to access the 'obj' variable in side the onclick listener.

Case 1

public void onBindViewHolder(MyAdapater.ViewHolder viewHolder, final int position) {

    MyObject obj= memberVariable.get(position);

    viewHolder.getButton().setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // use obj because I can access it
        }
    });
}

Case 2

public void onBindViewHolder(MyAdapater.ViewHolder viewHolder, final int position) {

    MyObject obj= memberVariable.get(position);

    viewHolder.getButton().setOnClickListener(this::approveItem);
}

private void approveItem(View view) {
// can't access the obj object now.
}

My onclicklistener will only take an interface with an onClick method, that takes a single parameter. So can't pass obj as part of that.

1
  • The first one is called a closure. Practically, by the looks, you could say that you are accessing a variable from an "outer" level of nested {} blocks. Commented Feb 26, 2022 at 14:25

2 Answers 2

1

What makes the difference here is just variable scope. Your anonymous class (new View.OnClickListener() {...}) being declared in the body of the method, it has access to its local variables (there are conditions, but those aren't relevant here).

You never can access a method-local variable from other methods. That just isn't allowed (just as it doesn't make sense).

When you use a method reference like that, you're effectively "borrowing" the method's code to provide an implementation of an interface (View.OnClickListener in your case). There is no way for that method to know about the local variables from where it's being called. And if you think about it, the method can be called from a different location where there is no such variable in the enclosing scope. So the compiler won't allow it.

The alternatives you have are:

Either use a lambda expression (assuming View.OnClickListener is acceptable as a functional interface):

viewHolder.getButton().setOnClickListener(view -> {
        // use obj because I can access it
});

Or, if the reason for code 2 is to externalize the logic, make the method take a second parameter:

private void approveItem(View view, MyObject obj) {
   // use obj
}

And pass it in the call

viewHolder.getButton().setOnClickListener(view -> this.approveItem(view, obj));
Sign up to request clarification or add additional context in comments.

Comments

1

In case 2, obj is obviously not in scope of the approveItem method. In case 1, the compiler implicitly provides variables of the outer scope to anonymous inner classes. If you don't want to use an anonymous inner class, you need to pass obj explicitly, which is not possible with a method reference:

public void onBindViewHolder(MyAdapater.ViewHolder viewHolder, int position) {
    MyObject obj = memberVariable.get(position);
    viewHolder.getButton().setOnClickListener(view -> this.approveItem(view, obj));
}

private void approveItem(View view, MyObject obj) {
    // ...
}

4 Comments

but my onClickListener would only take a single variable of type view. how do i go about doing this then, without an anonymous inner class. updated question with the same edit.
view -> this.approveItem(view, obj) is a View.OnClickListener whose functional interface method takes a single View parameter.
thanks. one final thing. 'view -> this.approveItem(view, obj)', is this a lambda expression, i.e. if I wanted to read more about how is it that the function takes two arguments (View and MyObject), but the View.onClickListener's onclick is supposed to take only one.
The formal parameter list of a lambda expression is found before the ->. The body (after the ->) can call arbitrary other code, e.g. a function with other parameters.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.