Kotlin与Android中内部类的作用及RecyclerView ViewHolder异常解析
Great question! Let's start by breaking down Kotlin's inner class functionality, then dive into the exact mechanics behind that RecyclerView ViewHolder issue you're seeing.
Kotlin handles nested classes differently from Java by default, and the inner keyword is the key to unlocking access to the outer class instance:
- Default nested classes (no
innerkeyword): These are effectively static nested classes. They don't hold a reference to the outer class instance, so they can only access static members of the outer class (like those in acompanion object). They're useful when you need a class that's logically tied to the outer class but doesn't depend on its state. - Inner classes (with
innerkeyword): These hold an implicit reference to the outer class instance (you can explicitly reference it withthis@OuterClassName). This lets them access all members of the outer class—including instance variables and non-static methods—just like you would from inside the outer class itself.
Let's tie this to your RecyclerView Adapter situation:
When you define a ViewHolder as a nested class inside your Adapter without the inner keyword, it's a static nested class. Here's why calling notifyDataSetChanged() breaks:
notifyDataSetChanged()is an instance method ofRecyclerView.Adapter—it requires a reference to the specific Adapter instance to work, since it operates on that Adapter's data and UI state.- A non-inner
ViewHolderhas no link to the Adapter instance that created it. It can't see or access the Adapter's instance methods, so the compiler throws an error saying it can't resolvenotifyDataSetChanged().
When you add the inner keyword:
- The
ViewHoldernow holds a reference to the parent Adapter instance. When you callnotifyDataSetChanged(), the compiler automatically resolves it tothis@YourAdapter.notifyDataSetChanged()(you can write that explicitly if you want clarity). This gives the method access to the correct Adapter instance, so the error disappears.
Example Code to Illustrate
class TodoAdapter(private val todos: List<Todo>) : RecyclerView.Adapter<TodoAdapter.TodoViewHolder>() { // Non-inner ViewHolder: Will throw an error when calling notifyDataSetChanged() class TodoViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { fun bind(todo: Todo) { itemView.setOnClickListener { // Error: Unresolved reference: notifyDataSetChanged // notifyDataSetChanged() } } } // Inner ViewHolder: Works perfectly inner class InnerTodoViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { fun bind(todo: Todo) { itemView.setOnClickListener { // Automatically uses the parent Adapter instance notifyDataSetChanged() // Explicit version: this@TodoAdapter.notifyDataSetChanged() } } } // ... Adapter overrides omitted for brevity }
A Quick Note on Alternatives
While using inner is the most straightforward Kotlin solution here, you could also:
- Pass a reference to the Adapter into the ViewHolder via its constructor
- Use an interface callback to trigger the update from the Adapter
Butinnerkeeps the code concise and aligned with Kotlin's design patterns—just be mindful of potential memory leaks if the ViewHolder is held in a long-lived context (though RecyclerView's ViewHolder pool usually mitigates this).
内容的提问来源于stack exchange,提问作者samuel ochuba

