How can I write this without kotlin SAM convertion?

an example where I cannot see a clean solution without SAM convertion for kotlin interfaces:

btw this code is a simplification of a real project. I want to keep the first create function (but we can change the signature of course)

— second edition : pass the function as parameter…

fun main(){
   val creator : ?????? = ::c
  createStringAndStringBuilder(creator)
}

fun <T:Any> c( clazz: Class<T> ) : T { return clazz.newInstance() }

fun createStringAndStringBuilder(creator:????) {
  create<String>(creator)
  create<StringBuilder>(creator)
}

inline fun <reified T:Any> create( creator: (Class<T>) -> T  ) : T  = creator( Class.forName( T::class.java.name ) as Class<T> ) as T

I don’t want to use noeither a java interface nor a object : MyInterface…

Like this?

fun <T> create(className:String, creator: (Class<T>) -> T ):T =
    creator(Class.forName(className) as Class<T>)


fun main() {
    val emptyString = create<String>("java.lang.String") { it.newInstance() }

    println("the string is empty: its length is ${emptyString.length}")
}

actually no. Creator should not be bound to of create… I want to be able to reuse creator:

let me add a second call to create with same creator:

//i'd like to change the signature to something like
//
// fun <T> create(className:String, <X>(Class<X>) -> X):T
// 
// ... but kotlin does not support generics in function parameter...
// using java Function<Class<X>, T> is cheating (it's like admitting we need SAMConvertion... but we don't have it for kotlin... imagine we need 3 input parameters...)
fun <T> create(className:String, creator:Creator):T =
    creator.create(Class.forName(className) as Class<T>)

fun main() {
    
    val creator = object:Creator {
    	override fun <T> create(clazz: Class<T>):T = clazz.newInstance()
    }
    
    //i'd like to write : val emptyString = create<String>("java.lang.String") { it.newInstance() }... but no sam convertion
    val emptyString = create<String>("java.lang.String", creator)
    val emptyStringBuilder = create<StringBuilder>("java.lang.StringBuilder", creator)


    println("the string is empty: its length is ${emptyString.length}")
    println("the stringBuilder is empty: its length is ${emptyStringBuilder.length}")
}

//i'd like to remove this interface (but kotlin does not support generic function as parameter... So...)
interface Creator {
    fun <T> create(clazz: Class<T>):T
}
fun <T:Any> create( className:String, creator: (Class<T>) -> T  ) : T  = creator( Class.forName( className ) as Class<T> ) as T

create<String>( "java.lang.String" ) { it.newInstance() }

And to reuse creator

   fun <T:Any> c( creator: Class<T> ) : T { return creator.newInstance() }
  create<String>("java.lang.String" , ::c )

And to avoid the first argument

inline fun <reified T:Any> create( creator: (Class<T>) -> T  ) : T  = creator( Class.forName( T::class.java.name ) as Class<T> ) as T

create<String>(::c)

so we cannot use a lambda in create call and we need a function reference… This is a big bad point vs java 8+…

this does not really solve my problem because in my real case, ::c is passed in function before been reused… so what is the type of ::c ?

what if I want to stock ::c anywhere ?

example :

fun main(){
   val creator : ?????? = ::c
  createStringAndStringBuilder(creator)
}

fun <T:Any> c( clazz: Class<T> ) : T { return clazz.newInstance() }

fun createStringAndStringBuilder(creator:????) {
  create<String>(creator)
  create<StringBuilder>(creator)
}

inline fun <reified T:Any> create( creator: (Class<T>) -> T  ) : T  = creator( Class.forName( T::class.java.name ) as Class<T> ) as T
1 Like

You could use creator: (Class<*>) -> Any or maybe this

inline fun <R: Any, reified T:R> create( creator: (Class<out T>) -> T  ) : R = creator(T::class.java)

You could use creator: (Class<*>) -> Any or maybe this

but then I would lose type safety ({Object()} would be a legal implementation)

your create method does not resolve the problem.

I’ll stick to object : MyInterface { override fun <T> create(x:Class<T>) = x.newInstance() } for now… or creating a java interface to be able to use SAM convertion…

not being able to use a lambda and so being less expressive than java feels bad…

You can get around the lambda limitation by declaring a typealias of a function, instead of a `fun. I think this would work for your case:

typealias Creator<T> = (Class<T>) -> T

fun <T> create(className: String, creator: Creator<T>):T = creator(Class.forName(className) as Class<T>)

fun main() {
    val emptyString = create<String>("java.lang.String") { it.newInstance() }
    println("the string is empty: its length is ${emptyString.length}")
}
1 Like

It’s not a creator of t, it’s a creator of *. I don’t want the creator to be limited. I just want the function to be typed. You cannot reuse your creator or you lose type safety.

1 Like