如何将 class 的实例与一种接口类型进行比较?
How can I compare an instance of a class to a type of interface?
我正在尝试基于 this article 在 Kotlin 中实现一个简单的服务定位器,但我想使用泛型类型参数。我还试图避免使用具体化的类型参数内联函数,因为这要求所有内容都是 public.
这个class负责缓存定位服务的实例:
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)
它 returns 每次都为 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
上,编译器抱怨 "Cannot check for instance of erased type: T"。如何在没有内联的情况下完成这项工作,这需要制作服务列表 public?
如果您同意反射,您可以使用 isAssignableFrom
来检查所请求的 Class
是否是您拥有的给定 Class
的 superclass/superinterface缓存:
fun <T> getService(serviceClass: Class<T>): T? {
return services.firstOrNull { s -> serviceClass.isAssignableFrom(s::class.java) } as T?
}
只是为了回答关于内联问题的另一半,您可以通过使用 @PublishedApi
注释和标记字段 internal
来避免制作服务映射 public。例如:
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)
}
}
感谢 kcoppock 的 @PublishedApi
注释的好主意,您也可以使用 is
,避免反射并使函数非常简洁:
class Cache {
@PublishedApi internal val services: MutableList<Any> = mutableListOf()
inline fun <reified T> getService() = services.firstOrNull { it is T } as T?
// ...
}
如果你想保持 services
私有(或使 getService
在 Java 中可用),并且仍然保持 Kotlin 更好的可用性,只需添加一个重载:
fun <T> getService(serviceClass: Class<T>): T? { ... }
inline fun <reified T> getService(): T? = getService(T::class.java)
我正在尝试基于 this article 在 Kotlin 中实现一个简单的服务定位器,但我想使用泛型类型参数。我还试图避免使用具体化的类型参数内联函数,因为这要求所有内容都是 public.
这个class负责缓存定位服务的实例:
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)
它 returns 每次都为 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
上,编译器抱怨 "Cannot check for instance of erased type: T"。如何在没有内联的情况下完成这项工作,这需要制作服务列表 public?
如果您同意反射,您可以使用 isAssignableFrom
来检查所请求的 Class
是否是您拥有的给定 Class
的 superclass/superinterface缓存:
fun <T> getService(serviceClass: Class<T>): T? {
return services.firstOrNull { s -> serviceClass.isAssignableFrom(s::class.java) } as T?
}
只是为了回答关于内联问题的另一半,您可以通过使用 @PublishedApi
注释和标记字段 internal
来避免制作服务映射 public。例如:
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)
}
}
感谢 kcoppock 的 @PublishedApi
注释的好主意,您也可以使用 is
,避免反射并使函数非常简洁:
class Cache {
@PublishedApi internal val services: MutableList<Any> = mutableListOf()
inline fun <reified T> getService() = services.firstOrNull { it is T } as T?
// ...
}
如果你想保持 services
私有(或使 getService
在 Java 中可用),并且仍然保持 Kotlin 更好的可用性,只需添加一个重载:
fun <T> getService(serviceClass: Class<T>): T? { ... }
inline fun <reified T> getService(): T? = getService(T::class.java)