9

I was trying to create an extension function to create object of view holder for recycler view adapter

inline fun <reified T: RecyclerView.ViewHolder> ViewGroup.createViewHolder(@LayoutRes res: Int): T {
    val inflater = LayoutInflater.from(context)
    val itemView = inflater.inflate(res, this, false)
    // return ViewHolder Object
}

How do I create an object of the type T that extends RecyclerView.ViewHolder so that I can return from the function.

Samuel Robert
  • 10,106
  • 7
  • 39
  • 60

3 Answers3

13

A clean alternative solution is to pass the constructor explicitly. It won't even be more verbose, because the type parameter can be inferred and doesn't need to be specified any more. Use like this:

val viewHolder = my_view_group.create(::MyViewHolder, R.layout.my_layout)

Implement like this:

inline fun <reified T: RecyclerView.ViewHolder> ViewGroup.create(createHolder: (View) -> T, @LayoutRes res: Int): T {
    val inflater = LayoutInflater.from(context)
    val itemView = inflater.inflate(res, this, false)
    return createHolder(itemView)
}
Robert Jack Will
  • 10,333
  • 1
  • 21
  • 29
5

This solution is pretty ugly, but I'm assuming that "theoretically" can work :

inline fun <reified T: RecyclerView.ViewHolder> ViewGroup.create(@LayoutRes res: Int): T {
        val inflater = LayoutInflater.from(context)
        val itemView = inflater.inflate(res, this, false)
        return T::class.java.getConstructor(View::class.java).newInstance(itemView)
    }

What the last line is doing is : 1. Get the constructor of T that matches T(view: View) 2. call newInstance on that constructor, passing it the view you inflated

Solution adapted from https://discuss.kotlinlang.org/t/generic-object-creation/1663/5

Simply call it via :

val viewHolder = my_view_group.create<MyViewHolder>(R.layout.my_layout)
NSimon
  • 5,212
  • 2
  • 22
  • 36
  • I understand this is the possible solution, but would like to know this is a good way to create ViewHolder? Is reflection a good thing? – Debanjan May 24 '18 at 14:23
  • Well, reflection is never really good. Here, many things could happen, such as an update to the way ViewHolder are created (not with a View as parameter for example). I'm not convinced you'll gain much from calling this instead of a simple MyViewHolder(LayoutInflater.from(parent.context).inflate(R.layout .my_layout, parent, false)) – NSimon May 24 '18 at 14:34
0

Not an answer to the question as stated but should be helpful to those arriving here wanting to get rid of Thing(Foo::class.java, ...) and use Thing<Foo>(...).

All you need to do is add a reified invoke in a companion object.

Before

class Thing<E : Enum<E>>(cls: Class<E>, value: String? = null) : Iterable<E> {
    ...
}

val thing = Thing(Foo::class.java, ...)

After

class Thing<E : Enum<E>>(cls: Class<E>, value: String? = null) : Iterable<E> {
    ...
    companion object {
        // Lets us construct using Thing<Foo>(...) instead of Thing(Foo::class.java, ...)
        inline operator fun <reified T : Enum<T>> invoke(value: String? = null) = Thing(T::class.java, value)
    }
}

val thing = Thing<Foo>(...)
OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213