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.
CodePudding user response:
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) {
// ...
}
CodePudding user response:
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));