如何将类的实例与接口的类型进行比较?

时间:2019-02-05 17:27:09

标签: design-patterns kotlin inversion-of-control service-locator

我试图基于this article在Kotlin中实现一个简单的服务定位器,但我想使用泛型类型参数。我还试图避免将函数与类型化参数内联,因为这要求所有内容都是公开的。

此类负责缓存已定位服务的实例:

class Cache {
    private val services: MutableList<Any> = mutableListOf()

    fun <T> getService(serviceClass: Class<T>): T? {
        val service = services.firstOrNull { s -> s::class.java == serviceClass }

        if (service != null) {
            return service as T
        }

        return null
    }

    fun addService(service: Any) {
        services.add(service)
    }
}

这是调用缓存的方式:

cache.getService(IMyService::class)

无论它是否包含MyService的实例,它每次都返回null。问题出在s::class.java == serviceClass上,因为在运行时缓存包含实例MyService,并且MyService::class.java不等于IMyService::classMyService::class也不是-我尝试过也是如此)。

我试图像这样修改getService方法:

fun <T> getService(): T? {
    val service = services.firstOrNull { s -> s is T }

    if (service != null) {
        return service as T
    }

    return null
}

s is T上,编译器抱怨“无法检查擦除类型T的实例”。我该如何在不进行内联的情况下进行这项工作,而这需要将服务列表公开?

4 个答案:

答案 0 :(得分:4)

如果您对反射没问题,则可以使用isAssignableFrom来检查请求的Class是否是已缓存的给定Class的超类/超接口:

fun <T> getService(serviceClass: Class<T>): T? {
    return services.firstOrNull { s -> serviceClass.isAssignableFrom(s::class.java) } as T?
}

答案 1 :(得分:2)

只需回答关于内联的另一半,您就可以避免使用@PublishedApi批注并标记字段internal来公开服务地图。例如:

class Cache {
    @PublishedApi internal val services: MutableList<Any> = mutableListOf()

    inline fun <reified T> getService(serviceClass: Class<T>): T? {
        return T::class.java.let { it.cast(services.firstOrNull(it::isInstance)) }
    }

    fun addService(service: Any) {
        services.add(service)
    }
}

答案 2 :(得分:2)

通过kcoppock的@PublishedApi注释,您也可以使用is,从而避免了反射,并使函数非常简洁:

class Cache {
    @PublishedApi internal val services: MutableList<Any> = mutableListOf()

    inline fun <reified T> getService() = services.firstOrNull { it is T } as T?

    // ...
}

答案 3 :(得分:0)

如果您想保持services私有(或要使getService在Java中可用),并且仍然保持Kotlin的更好可用性,只需添加一个重载即可:

fun <T> getService(serviceClass: Class<T>): T? { ... }

inline fun <reified T> getService(): T? = getService(T::class.java)