0

Consider this snipped:

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    val view = inflater.inflate(R.layout.test, container, false)

    class MyViewHolder(val view: View): RecyclerView.ViewHolder(view) {
        init {
            view.setOnClickListener {
                Log.d("hey", "there")
            }
        }
    }

    view.findViewById<RecyclerView>(R.id.files).adapter = object: RecyclerView.Adapter<MyViewHolder>() {
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
            val item = LayoutInflater.from(parent.context).inflate(R.layout.item, parent, false)
            return MyViewHolder(item)
        }

        override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
            holder.view.findViewById<TextView>(R.id.text).text = files[position].name
        }

        override fun getItemCount() = files.size
    }

    return view
}

onCreateView creates a local variable view. The nested class MyViewHolder also declares a variable called view. What was unexpected is that the variable view accessed inside the init block of MyViewHolder (where the OnClickListener is set) is not the one declared in MyViewHolder, but the outer one. Why?

  1. I would expect the innermost variable declaration would be used.

  2. The class is not declared as an inner class. Outside variables should not be accesible.

What am I missing?

user1785730
  • 3,150
  • 4
  • 27
  • 50

1 Answers1

1

This is not the case of a nested class, but of a function that returns a class.

If you try to define your class as inner, you'll actually get an error message:

Modifier 'inner' is not applicable to 'local class'

I'll simplify this example a bit, so the Android part won't interfere:

// This is what you're doing
fun a(): Any {
    val a = "a"

    class B(val a: String = "b") {
        init {
            println(a)
        }
    }

    return B()
}

// This is what you think you're doing
class A(val a: String = "a") {
    class B(val a: String = "b") {
        init {
            println(a)
        }
    }
}

fun main() {
    // This refers to function called a
    val func = a()
    
    // This refers to a nested class called B
    val nestedClass = A.B()
}

If you actually want to refer to the local class properties, use this

Alexey Soshin
  • 16,718
  • 2
  • 31
  • 40
  • I don't follow. Why is it not a nested class? Because it's inside a function? – user1785730 Mar 17 '21 at 20:58
  • Looking at the [documentation for nested classes](https://kotlinlang.org/docs/nested-classes.html) I see two differences: The nested class is directly nested in the parent class, not inside a function; and the nested class does not take parameters. Which one of these disqualifies `B` from being a nested class? – user1785730 Mar 17 '21 at 21:02
  • 1
    It's not directly nested in a parent class. It's nested inside a function. It's a `local class`, which is the same as `inner class` I updated my example, in hopes it will explain the behavior better. If you copy it to your IDE, you'll notice how it points out which variable will be used in each case. – Alexey Soshin Mar 17 '21 at 21:05
  • I think I'm starting to understand this a little better. However, I also think that there is a misunderstanding because my code snipped was not complete. I've updated it. The point is, I'm using `B` only inside function `a`, and `a` doesn't return `B`, but something else. – user1785730 Mar 17 '21 at 21:23
  • 1
    Your `MyViewHolder` is still inside a function. So, it's still a `local class`/`inner class`. – Alexey Soshin Mar 17 '21 at 21:26