【问题标题】:How can I compare an instance of a class to a type of interface?如何将类的实例与接口类型进行比较?
【发布时间】:2019-02-05 17:27:08
【问题描述】:

我正在尝试基于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”。我如何在没有内联的情况下完成这项工作,这需要公开服务列表?

【问题讨论】:

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


    【解决方案1】:

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

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

    【讨论】:

    • 或者serviceClass.isInstance(s) 会做同样的事情。
    • 在这种情况下我有什么理由要避免反思吗?
    • 我认为这里没有比其他任何地方更多的理由来避免它,如果你不使用 reified,这就是这样做的方法。出于各种原因,有些人只是倾向于避免反思。
    【解决方案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)
        }
    }
    

    【讨论】:

      【解决方案3】:

      感谢 kcoppock 的 @PublishedApi 注释的好主意,您也可以使用 is,避免反射并使函数真正简洁:

      class Cache {
          @PublishedApi internal val services: MutableList<Any> = mutableListOf()
      
          inline fun <reified T> getService() = services.firstOrNull { it is T } as T?
      
          // ...
      }
      

      【讨论】:

        【解决方案4】:

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

        fun <T> getService(serviceClass: Class<T>): T? { ... }
        
        inline fun <reified T> getService(): T? = getService(T::class.java)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-11-08
          • 2015-10-25
          • 1970-01-01
          • 1970-01-01
          • 2012-05-19
          • 2015-01-31
          • 2015-06-24
          • 1970-01-01
          相关资源
          最近更新 更多