Tran Hoai Nam
Tran Hoai Nam

Reputation: 1363

Android - "No enclosing instance of type 'some.abstract.class.name' class is in scope" error when extended

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!

Upvotes: 2

Views: 492

Answers (1)

ajb
ajb

Reputation: 31699

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.

Upvotes: 1

Related Questions