我试图实现科特林一个简单的服务定位器,基于this article,但我想使用泛型类型参数。我也想避免与具体化类型参数内联函数,因为需要的一切是公开的。
这个类是负责的位于服务缓存的实例:
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)
它返回null每次,不管它是否包含MyService
与否的实例。问题是在s::class.java == serviceClass
,因为在运行时的缓存中包含一个实例MyService
,并MyService::class.java
不等同于IMyService::class
(也不是MyService::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”。我怎样才能使这项工作不内联,这需要服务的名单公之于众?
如果您能够接受的反射,你可以使用isAssignableFrom
检查请求的Class
是你缓存的给定Class
的超类/超接口:
fun <T> getService(serviceClass: Class<T>): T? {
return services.firstOrNull { s -> serviceClass.isAssignableFrom(s::class.java) } as T?
}
只是回答关于内联问题的另一半,你能避免使用@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)
}
}
由于与@PublishedApi
注释,你可以使用is
为好,避免反射,使功能确实简洁kcoppock的好主意:
class Cache {
@PublishedApi internal val services: MutableList<Any> = mutableListOf()
inline fun <reified T> getService() = services.firstOrNull { it is T } as T?
// ...
}
如果你想保持私人services
(或使getService
从Java可用),并且仍然保持从科特林更好的可用性,只需添加一个过载:
fun <T> getService(serviceClass: Class<T>): T? { ... }
inline fun <reified T> getService(): T? = getService(T::class.java)