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

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

  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
Android Studio 3 версия Kotlin Сеттеры и геттеры в Котлине Создание исполняемой толстой баночки с град-скрипт-котлин Как сравнить Short с Int в Kotlin? Как обрабатывать обработку ошибок в одном месте в rxjava с помощью обертки Как создать пользовательскую форму кнопки, но сохранить ее кликабельную и настраиваемую Анимация (прокрутка) одна ViewGroup блокирует несвязанные виды из анимации до завершения Анализ данных о проблемах с RxJava + Kotlin проекции не допускаются для непосредственных подтипов супертипа лучший способ проверить null в kotlin? Есть ли какая-нибудь библиотека для работы с монадами на котлин? Как я могу использовать KotlinJS без stdlib? Ошибка: Kotlin: несколько значений недопустимы для опции плагина. Org.jetbrains.kotlin.kapt: output RecyclerView с Kotlin не выставляет ячейки по вертикали JsonArray в класс данных Kotlin с использованием Retrofit (ожидается BEGIN_OBJECT, но BEGIN_ARRAY)