Instantiating a generic type in Kotlin

2019-02-13 04:03发布

问题:

What's the best way to get an instance of a generic type in Kotlin? I am hoping to find the best (if not 100% perfect) approximation of the following C# code:

public T GetValue<T>() where T : new() {
    return new T();
}

回答1:

EDIT: As mentioned in comments, this is probably a bad idea. Accepting a () -> T is probably the most reasonable way of achieving this. That said, the following technique will achieve what you're looking for, if not necessarily in the most idiomatic way.

Unfortunately, you can't achieve that directly: Kotlin is hamstrung by its Java ancestry, so generics are erased at run time, meaning T is no longer available to use directly. Using reflection and inline functions, you can work around this, though:

/* Convenience wrapper that allows you to call getValue<Type>() instead of of getValue(Type::class) */
inline fun <reified T: Any> getValue() : T? = getValue(T::class)

/* We have no way to guarantee that an empty constructor exists, so must return T? instead of T */
fun <T: Any> getValue(clazz: KClass<T>) : T? {
    clazz.constructors.forEach { con ->
        if (con.parameters.size == 0) {
            return con.call()
        }
    }
    return null
}

If we add some sample classes, you can see that this will return an instance when an empty constructor exists, or null otherwise:

class Foo() {}
class Bar(val label: String) { constructor() : this("bar")}
class Baz(val label: String)

fun main(args: Array<String>) {
    System.out.println("Foo: ${getValue<Foo>()}") // Foo@...
    // No need to specify the type when it can be inferred
    val foo : Foo? = getValue()
    System.out.println("Foo: ${foo}") // Foo@...
    System.out.println("Bar: ${getValue<Bar>()}") // Prints Bar@...
    System.out.println("Baz: ${getValue<Baz>()}") // null
}


标签: kotlin