What is the proper way to create new instance of generic class in kotlin?
Asked Answered
S

2

38

I use following initialization:

val entityClass = javaClass<Class<T>>()
var entity = entityClass.newInstance().newInstance()

but it's wrong and causes IllegalAccessException on java.lang.Class.newInstance(Class.java:1208)

Shoemake answered 18/11, 2014 at 10:29 Comment(0)
H
60

If you let IntelliJ add explicit type information, you see that entityClass is actually of type Class<Class<String>>. I'm not sure if that's what you want. In line 2 you are first creating an instance of Class<T> and then one of T but that's not possible anyway, because the generic information about T is lost at runtime. Apart from that you can't instantiate class objects directly.

Solution

One possible solution would be to add a parameter of type Class<T> to your function or class and use it to instantiate objects like this.

fun <T> foo(entityClass: Class<T>) {
    var entity: T = entityClass.newInstance()
}

fun test() {
    foo(Object::class.java)
}

But there's actually a more elegant solution without the use of reflection. Define a parameter of method type () -> T and use constructor references. Here's my related question about constructor references and here's the code:

fun <T> foo2(factory: () -> T) {
    var entity: T = factory()
}

fun test() {
    foo2(::Object)
}
Huldahuldah answered 18/11, 2014 at 17:45 Comment(4)
hi, what if entityClass is Interface?Disarming
Is there a way to set a bound on the generic type that says "not an interface"? C# has the new keyword for similar behavior, to say "T must be constructable".Sarena
I guess you can omit entityClass by making the function inline with reified.Ejaculation
How can attributes be added to the Object when created in the foo function?Dildo
U
7

Thanks to Kirill Rakhman I wrote a similar answer (for Android adapter). A difference here is in a parameter of the class.

private fun <T> createItem(
    viewGroup: ViewGroup,
    layoutRes: Int,
    method: (View) -> T
): T {
    val view = LayoutInflater.from(viewGroup.context).inflate(layoutRes, viewGroup, false)
    return method(view) // Creates T(view).
}

Then use it this way:

override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): AbstractViewHolder {
    return when (viewType) {
        LOADER -> createItem(viewGroup, R.layout.row_loader, ::LoaderViewHolder)
        DATE -> createItem(viewGroup, R.layout.row_date, ::DateViewHolder)
        else -> throw IllegalStateException("Wrong class")
    }
}

Here LoaderViewHolder and DateViewHolder are descendants of AbstractViewHolder.

Undershoot answered 1/9, 2020 at 15:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.