Итак, код ниже – это система событий, которая выполняет следующие действия:
Код выглядит следующим образом:
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», но я попробовал реализацию с ним, и все было в порядке; нет настоящей проблемы, если вы захотите.