Как я могу сравнить экземпляр класса с типом интерфейса? - PullRequest
0 голосов
/ 05 февраля 2019

Я пытаюсь реализовать простой сервисный локатор в Kotlin, основанный на этой статье , за исключением того, что я хочу использовать параметры универсального типа.Я также пытаюсь избежать встраивания функций с параметрами типа reified, потому что для этого требуется, чтобы все было общедоступным.

Этот класс отвечает за кэширование экземпляров расположенных служб:

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 или нет.Проблема в 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 голосов
/ 06 февраля 2019

Если вы хотите сохранить services в секрете (или сделать getService пригодным для использования из Java) и при этом сохранить удобство использования из Kotlin, просто добавьте перегрузку:

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

inline fun <reified T> getService(): T? = getService(T::class.java)
0 голосов
/ 05 февраля 2019

Просто чтобы ответить на другую половину вопроса о встраивании, вы можете не делать общедоступной карту служб, используя аннотацию @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)
    }
}
0 голосов
/ 05 февраля 2019

Благодаря замечательной идее kcoppock с аннотацией @PublishedApi вы также можете использовать is, избегая отражения и делая функцию действительно краткой:

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

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

    // ...
}
0 голосов
/ 05 февраля 2019

Если вы согласны с отражением, вы можете использовать isAssignableFrom, чтобы проверить, является ли запрошенный Class суперклассом / суперинтерфейсом заданного Class, который вы кэшировали:

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