0

I followed these instructions to create a simple service locator, adapted to Kotlin and simplified even more for the purpose of my question:

object ServiceLocator {

    @JvmStatic
    fun getService(serviceName: String): Any {
        val service = lookupService(serviceName)

        if (service != null) {
            return service
        }

        throw UnknownServiceException()
    }

    private fun lookupService(serviceName: String): Any? {
        if (serviceName.equals("MyService")) {
            return MyService()
        }

        return null
    }
}

I can use it as follows:

val myService = ServiceLocator.getService("MyService") as IMyService

The problem I have with this is myService is of type Any. I'd prefer it if getService returned the actual type instead. Ideally, I'd like to use it like so:

val myService = ServiceLocator.getService<IMyService>()

Or, if that's not possible, something like this would be fine:

val myService = ServiceLocator.getService(IMyService::class)

I can't figure out how to make the generic type parameters work. This is what I tried:

@JvmStatic
fun <T> getService(): T {
    val service = lookupService<T>()

    if (service != null) {
        return service
    }

    throw UnknownServiceException()
}

private fun <T> lookupService(): T? {
    if (T::class == IMyService::class) {
        return MyService()
    }

    return null
}

The compiler is complaining about two things:

  1. On T::class, it complains "Cannot use 'T' as reified type parameter. Use a class instead."
  2. On return MyService(), it says "Type mismatch. Required: T? Found: MyService"
Big McLargeHuge
  • 14,841
  • 10
  • 80
  • 108

1 Answers1

2

To access the type, T, at runtime without having to pass the class in (e.g. ServiceLocator.getService(IMyService::class)), you have to use a reified type parameter. Implicitly this means you have to change your methods to be inline functions. Doing that you could update your code to the below:

@JvmStatic
inline fun <reified T> getService(): T {
    val service = lookupService<T>()

    if (service != null) {
        return service
    }

    throw UnknownServiceException()
}

inline fun <reified T> lookupService(): T? {
    if (T::class == IMyService::class) {
        return MyService() as T
    }

    return null
}
Yoni Gibbs
  • 6,518
  • 2
  • 24
  • 37
  • This means `lookupService` has to be public as well. Is it possible to keep it private? – Big McLargeHuge Feb 05 '19 at 16:51
  • See the discussion [here](https://stackoverflow.com/questions/41892715/inline-function-cannot-access-non-public-api-publishedapi-vs-suppress-vs-jvm). Hope that helps. – Yoni Gibbs Feb 06 '19 at 08:26