Ввод общей лямбды в карту

Итак, код ниже – это система событий, которая выполняет следующие действия:

  1. Присваивает целочисленный id выражению лямбда
  2. Помещает идентификатор лямбды в изменяемый набор событий
  3. Отображает целочисленный идентификатор в выражение лямбда
  4. Возвращает идентификатор (может использоваться позже для удаления событий из лямбда)

Код выглядит следующим образом:

class EventHandler { companion object { val handlers = HashMap<KClass<out Event>, MutableSet<Int>>() val idMap = HashMap<Int, (Event) -> Unit>(); /** * @param event Class of the event you are registering * @param handler What to do when the event is called */ fun <T : Event> register(event: KClass<T>, handler: (T) -> Unit): Int { var id: Int = 0; while(idMap[id] != null) { id++; } var list = handlers.getOrPut(event, {mutableSetOf()}); list.add(id); idMap[id] = handler; return id; } } } 

Предполагаемое использование этого метода будет примерно таким:

 EventHandler.register(ChatEvent::class) { onChat -> println(onChat.message) } 

На следующей строке есть ошибка: idMap[id] = handler;

Ошибка заключается в том, что обработчик имеет тип (T) -> Unit , хотя он должен быть (Event) -> Unit , чтобы добавить его в idMap . Хотя я сказал, что T должен продлить Event когда я его создал, поэтому это не должно быть проблемой. Кто-нибудь знает, почему это происходит, если есть решение?

Проблема возникает из-за того, что idMap принимает функцию, которая получает Eventлюбое Event . Затем вы пытаетесь зарегистрировать функцию, которая принимает указанный подкласс Event , который, когда вы вытаскиваете его обратно, компилятор не сможет определить, какой возможный подкласс он получает. Да, вы сохраняете конкретный тип на другой карте, но компилятор не может этого использовать.

Я не верю, что вы можете создать систему сопоставления, которую вы хотите создать. Не без нескольких слоев косвенности или абстракции …

Причина, по которой вы получаете эту ошибку, хорошо объясняется @ jacob-zimmerman в его ответе : (T) -> Unit не является подтипом (Event) -> Unit (напротив, это супертип).

Вы можете сделать необработанное понижение до требуемого типа функции:

 idMap[id] = handler as (Event) -> Unit 

Но перед вызовом такого обработчика вы должны проверить, что тип события имеет тип, который может принять обработчик, например, путем запроса обработчика из карты в зависимости от типа события:

 fun invoke(event: Event) { val kclass = event.javaClass.kotlin val eventHandlers = handlers[kclass]?.map { idMap[it]!! } ?: return eventHandlers.forEach { it.invoke(event) } } 

Я понял реализацию:

 class Handlers<T: Event> { val backingList = ArrayList<(T) -> Unit>() fun add(handler: (T) -> Unit) { backingList.add(handler) } fun remove(handler: (T) -> Unit) { backingList.remove(handler) } fun handleEvent(event: T) { backingList.forEach { handle -> handle(event) } } } class HandlerMap { val handlerMap = HashMap<KClass<out Event>, Handlers<out Event>>() fun <T : Event> register(event: KClass<T>, handler: (T) -> Unit) { val list: Handlers<T> if(!handlerMap.containsKey(event)) { list = Handlers<T>() handlerMap.put(event, list) } else { list = handlerMap.get(event) as Handlers<T> } list.add(handler) } fun <T: Event> getHandlers(event: KClass<T>): Handlers<T> { return handlerMap.get(event) as Handlers<T> } } 

Он должен сделать некоторое литье из-за того, что Карта является открытой, но система закрыта, поэтому объект Handlers гарантированно будет иметь точно правильный тип. Из-за изменения реализации я был уверен, что вам больше не нужен индекс / «id», но я попробовал реализацию с ним, и все было в порядке; нет настоящей проблемы, если вы захотите.

Intereting Posts
Kotlin на Android – существует ли минимальное требование уровня API? Kotlin Ошибка: не удалось найти org.jetbrains.kotlin: kotlin-stdlib-jre7: 1.0.7 Kotlin не может вывести тип при использовании ссылки метода в Flowable Как получить результат функции огурца Как решить: «ошибка: не удается найти класс символов …» после преобразования класса Java в Kotlin? Извлечение URL-адреса в Android Kotlin асинхронно Как определить длинный тип данных в котлин? Настройка Kotlin при выражении Разница между нитью и сопрограммой в Котлине Не удалось обновить значение параметра параметра метода Котлин нет конструктора по умолчанию для объекта JPA с Kotlin даже с плагином noarg Saripaar formvalidation не работает второй раз в котлине Как использовать Spek Как использовать «Функциональное определение компонента Kotlin DSL» с Spring Boot и Spring WebFlux? Spring Data Elasticsearch с полем скриптов с данными класса данных Kotlin приводит к проблеме сериализации Джексона