2

I have an abstract adapter class from an external library:

public abstract class DragItemAdapter<T, VH extends DragItemAdapter.ViewHolder> extends RecyclerView.Adapter<VH> {
    //Their other codes
    public class ViewHolder extends RecyclerView.ViewHolder {
        public ViewHolder(final View itemView, int handleResId) {
            super(itemView);
            //The rest of their codes
        }
    }
}

And I have my Adapter extended that adapter

public class ChecklistAdapter extends DragItemAdapter<Pair<Integer, SomeClass>, ViewHolderForChecklist> {

    @Override
    public ViewHolderForChecklist onCreateViewHolder(ViewGroup parent, int viewType) {
          View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
          grab = R.id.grab;
          return new ViewHolderForChecklist(view,grab);
    }
}

If my ViewHolderForChecklist is an inner class of the ChecklistAdapter it works fine. But if I move the ViewHolderForChecklist to a brand new class

public class ViewHolderForChecklist extends DragItemAdapter<Pair<Long, SomeClass>, ViewHolderForChecklist>.ViewHolder { // The error is at this line

    public ViewHolderForChecklist(final View itemView, int grab) {
        super(itemView, grab);
    }

    @Override
    public void onItemClicked(View view) {

    }

    @Override
    public boolean onItemLongClicked(View view) {
        return true;
    }
}

There is an error in real time

No enclosing instance of type 'library.package.name.DragItemAdapter' class is in scope

and the error when compile

error: an enclosing instance that contains DragItemAdapter.ViewHolder is required

Using "move" from Refractor has the same problem. I'm still new to this kind of... 'nested-class" so I don't know what is wrong with this or what kind of info should I include more.

Thank you!

Tran Hoai Nam
  • 1,273
  • 2
  • 18
  • 35

1 Answers1

1

ViewHolder is an inner class of DragItemAdapter (because it wasn't declared static). That means that every object of class ViewHolder must be associated with an object of class DragItemAdapter (actually, it would have to be a subclass of DragItemAdapter). You can think of ViewHolder having a hidden instance variable like

DragItemAdapter __outerObject;

The ViewHolder can directly access instance variables and methods belonging to the __outerObject.

That means that when you say new ViewHolder(...), you have to have some DragItemAdapter for the ViewHolder to be associated with.

The same applies to any subclass of ViewHolder, including ViewHolderChecklist, since the subclass inherits the hidden __outerObject variable.

In the first example, where ViewHolderChecklist is inside a ChecklistAdapter, the onCreateViewHolder method will always be called on a ChecklistAdapter instance. When that method says new ViewHolderChecklist, the new object's __outerObject will be set to the ChecklistAdapter instance. Also, if an outside class has a ChecklistAdapter adapter;, it can use that to create a new ViewHolderChecklist by saying adapter.new ViewHolderChecklist(...).

When you move ViewHolderChecklist outside the class, though, there's no way for a new instance to be created, since there's no way to use new in a way that would tell it what its __outerObject is supposed to be. The adapter.new ViewHolderChecklist(...) syntax won't work, because that syntax is only allowed for nested classes, and ViewHolderChecklist isn't a nested class. So ViewHolderChecklist has to be a nested class inside a subclass of DragItemAdapter.

Correction: It's actually possible to declare ViewHolderChecklist like this. However, you have to give it an explicit constructor and it has to have a Qualified Superclass Constructor Invocation (see this; see also https://stackoverflow.com/questions/40501642/what-rule-prohibits-this-nested-class-reference/40501815.

Community
  • 1
  • 1
ajb
  • 31,309
  • 3
  • 58
  • 84
  • Thanks for your detailed explanation. Your conclusion of "has to be a nested class" is what proves it impossible and it saves a lot of time looking for a solution. – Tran Hoai Nam Nov 09 '16 at 07:02
  • Please see my correction, although most likely it involves a construct you wouldn't want to use. – ajb Nov 09 '16 at 07:24
  • Thank you, I'll take a look at that. – Tran Hoai Nam Nov 11 '16 at 07:25
  • Wow, just edit a few lines and it works. Pass an instance of `ChecklistAdapter` to the viewholder `return new ViewHolderForChecklist(view,grab,this);` then use that instance to call super `adapter.super(itemView, grab);` in `ViewHolderForChecklist` class. But can you tell me why you said "you wouldn't want to use"? I think it's kinda simple – Tran Hoai Nam Nov 11 '16 at 07:36
  • 1
    Well, then, **I** wouldn't want to use it. It seems to me that it could make the code more obscure. On the other hand, if there are good reasons to do things that way, those reasons might outweigh my objections. Maybe someday I'll run across a case where I'll want to use this. – ajb Nov 12 '16 at 06:56