38

What's the best way to get an instance of a generic type in Kotlin? I am hoping to find the best approximation of the following C# code:

public T GetValue<T>() where T : new() {
    return new T();
}
ikh
  • 2,336
  • 2
  • 19
  • 28
  • 5
    Just receive a `() -> T` as a parameter for your method. – Yoav Sternberg Apr 18 '17 at 17:15
  • 4
    It's generally considered best practice to pass in a `() -> T` anywhere you would use such code, and let the call site handle instantiation, since you can't guarantee the `T` will have a no-arg constructor. – Ruckus T-Boom Apr 18 '17 at 17:16
  • 2
    @YoavSternberg You beat me by 20 seconds :) – Ruckus T-Boom Apr 18 '17 at 17:16
  • Possible duplicate of [What is the proper way to create new instance of generic class in kotlin?](http://stackoverflow.com/questions/26992039/what-is-the-proper-way-to-create-new-instance-of-generic-class-in-kotlin) – Kirill Rakhman Apr 19 '17 at 07:41

1 Answers1

29

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:

/* We have no way to guarantee that an empty constructor exists, so must return T? instead of T */
inline fun <reified T : Any> getValue(): T? {
    val primaryConstructor = T::class.constructors.find { it.parameters.isEmpty() }
    return primaryConstructor?.call()
}

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
}
Cristan
  • 12,083
  • 7
  • 65
  • 69
pdpi
  • 4,163
  • 2
  • 21
  • 30
  • 9
    Thanks for your answer. Looks like they weren't able to bring in the best from all modern languages then ... as a C# developer looking at Kotlin this is a bit of a letdown. They should have found some way to work some magic into the runtime. – dodgy_coder Feb 03 '19 at 13:10
  • 11
    It's crazy that the decision to implement generics in Java via type erasure to ensure backwards compatibility has had such long-term negative effects on the JVM ecosystem. – david.barkhuizen Apr 05 '19 at 13:32
  • 1
    This is not possible in Kotlin version `1.4.32`. I get this exception when I try to call `T::class.java.constructors`: `This function has a reified type parameter and thus can only be inlined at compilation time, not called directly.` – cbaldan Jul 15 '21 at 02:18