How to create object of a reified type in Kotlin
M

3

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.

Mongol answered 24/5, 2018 at 13:42 Comment(0)
P
15

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)
}
Plangent answered 24/5, 2018 at 14:33 Comment(1)
You can refer to the constructor too with :: (function reference)? I didn't know that, cool!Pelpel
P
6

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)
Physics answered 24/5, 2018 at 14:3 Comment(2)
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?Isogloss
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))Physics
H
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>(...)
Halvorsen answered 1/1, 2021 at 8:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.