Is there anyway to get the KClass that corresponds...
# announcements
s
Is there anyway to get the KClass that corresponds to a given functional type? Because Kotlin doesn’t do SAM conversion for Interfaces, I’m kinda unsure how to pass a functional type to another function that accepts a KClass. I’m primarily interested in using this to bind a singleton to a function type for the purposes of injection.
z
You could use reified generics
s
Copy code
inline fun <reified T> foo() = funThatTakesKClass(T::class)
Works for functional types?
d
Yes it does, but note that functional types are erased.
() -> String
and
() -> Unit
are the same class at runtime.
s
Yeah I suspected that would happen. It’s really quite irritating that there’s no permanent way to name a functional type.
While also allowing SAM conversions to work
d
Note also that this is an implementation detail. I don't think the JS platform for example uses different classes at runtime, because lambdas are represented as plain functions there.
z
You could also use KType instead of KClass, which will remember type parameters. See
typeOf()
d
On the JVM you can also use the interfaces defined in
kotlin.jvm.functions
directly, such as
Function0::class
That makes it more explict that this is a JVM-specific feature
s
Huh, let me try that. I had no idea that existed. Are these details documented somewhere?
d
Actually
Function1::class
works on JS also. But you might not be able to detect that something is of that type at runtime (as opposed to the JVM). Let me try some more.
s
Yeah a lot of this depends on how the injection framework works (I’m trying to use Koin here), but generally if the type is erased then it will probably fail to inject that type.
Hence why I’m trying to implement a functional interface
d
Koin is based on inline functions and reified, from my experience with it this should work.
It's just runtime checks that won't work, which Koin does not do, as far as I know.
E.g. the following:
Copy code
val lambda = { foo: String -> foo.toUpperCase() }
println(lambda is Function1)
println(lambda is Function0<*>)
Prints
true
,
false
on JVM,
true
,
true
on JS.
s
Ah, I guess I’ll try using the reified types.
And yeah, makes sense to me
d
Koin does so by default already
if you do
single<Foo> { ... }
that
Foo
is reified
Same with
get<Foo>()
s
It’d be super nice if the compiler could synthesize a backing functional interface for functional types
d
It does, thats what those
Function0
, etc. things are.
But that only works on the JVM.
(Not sure about native)
s
It doesn’t look like it works correctly with SAM though
Copy code
interface Foo: Function1<String, String> {
    override operator fun invoke(it: String): String
}

object bar: Foo { it ->
  // does not work
}
d
SAM is only supported for Java interfaces.
Until we get the new type inference in 1.4 at least
s
Yeah I guess I don’t really understand why that is, unless it’s just unimplemented but planned
d
What were you trying to achieve in that example?
s
Avoiding the boilerplate of explicitly redefining
invoke
as whenever I want to create an instance of
Foo
instead it would be nice to just treat
Foo
as a function, but with a runtime name.
But if koin does everything with reified types it’s irrelevant to my usecase
d
Yes, you can just use plain function types. However you can also create your own interfaces and not extend
FunctionX
(you probably shouldn't do that anyways).
operator fun invoke
works even without extending
FunctionX
.
And you can "fake" SAM conversion as follows:
Copy code
interface Foo {
    operator fun invoke(arg: String): String
    companion object {
        inline operator fun invoke(crossinline body: (String) -> String): Foo {
            return object : Foo {
                override operator fun invoke(arg: String) = body(arg)
            }
        }
    }
}
val myFoo = Foo { arg -> arg.toUpperCase }
And then
val resultOfMyFoo = myFoo("hello world") // "HELLO WORLD"
s
Ah very interesting, I’ll keep that in mind!
Thanks