Reputation: 11835
I'm trying to write in Kotlin something like the following Java code:
interface Provider {
}
class ProviderImpl1 implements Provider {
}
class ProviderImpl2 implements Provider {
}
enum Providers {
ONE(ProviderImpl1::new),
TWO(ProviderImpl2::new);
private final Supplier<Provider> supplier;
Providers(Supplier<Provider> supplier) {
this.supplier = supplier;
}
public Provider provider() {
return supplier.get();
}
}
This code compiles and works correctly: Providers.ONE
produces an instance of ProviderImpl1
, and Providers.TWO
gives an instance of ProviderImpl2
.
Here is what I could achieve in Kotlin:
interface Provider {
}
class ProviderImpl1 : Provider {
}
class ProviderImpl2: Provider {
}
enum class Providers(private val factory: Supplier<Provider>) {
ONE(Supplier{ ProviderImpl1() }),
TWO(Supplier{ ProviderImpl2() });
fun provider(): Provider = factory.get()
}
It works, but in Java I'm able to use a constructor reference in an enum constructor. When I try to do the same in Kotlin, namely
ONE( ::ProviderImpl1 ),
I get the following compilation error:
Type mismatch: inferred type is KFunction0 but Supplier was expected
A lambda without explicit type does not work either:
ONE( ::ProviderImpl1 )
gives
Type mismatch: inferred type is () -> ProviderImpl1 but Supplier was expected
The question is: does Kotlin specification prohibit this (and if yes, why, as Java seems to cope with it), or this is just a temporary imperfection of the current Kotlin compiler?
My build.gradle
has the following
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.2.61'
}
Kotlin language version is displayed by Idea (in project settings) as 1.2.
Upvotes: 2
Views: 1146
Reputation: 31670
If you change your Supplier
to a lambda, this can be achieved quite nicely in Kotlin...
interface Provider
class ProviderImpl1 : Provider
class ProviderImpl2 : Provider
enum class Providers(private val supplier: () -> Provider) {
ONE({ ProviderImpl1() }),
TWO({ ProviderImpl2() });
fun provider(): Provider = supplier.invoke()
}
The change here is to hand in a function that returns a Provider
instance (which is essentially what a Suppiler
is). This is nice because if, in the future, your Provider
implementation needs some kind of configuration as it is being constructed, this lambda can handle that.
If your provider is stateless, you could get away with changing Providers.provider()
into a val
, where it will only be created once per enum type.
Upvotes: 3
Reputation: 14601
You can use kotlin.reflect.KFunction0
like the compilation error suggests instead of java.util.function.Supplier
and then you can use the method reference.
Example:
import kotlin.reflect.KFunction0
interface Provider {
}
class ProviderImpl1 : Provider {
}
class ProviderImpl2: Provider {
}
enum class Providers(private val factory: KFunction0<Provider>) {
ONE(::ProviderImpl1),
TWO(::ProviderImpl2);
fun provider(): Provider = factory.call()
}
In this case the error message suggests that it expects an interface kotlin.reflect.KFunction0
other than java.util.function.Supplier
, so there is no prohibition for using method references in this constructor. You can use it, you just need to use the expected interface.
Upvotes: 2