- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我是 Kotlin 的新手,我正在使用它。我非常想创建一个非常基本的事件总线。所以我想出了这个
interface Event
interface EventListener<E : Event> {
fun handle(event: E)
}
interface EventBus {
fun <E : Event> registerListener(aClass: Class<E>, eventListener: EventListener<E>)
}
class MyBus() : EventBus {
private val eventListeners: MutableMap<String, MutableList<EventListener<out Event>>> = mutableMapOf()
constructor(listeners: List<Pair<Class<Event>, EventListener<Event>>>) : this() {
listeners.forEach {
registerListener(it.first, it.second)
}
}
override fun <E : Event> registerListener(aClass: Class<E>, eventListener: EventListener<E>) {
val key = aClass.name
val listeners: MutableList<EventListener<out Event>> = eventListeners.getOrPut(key) { mutableListOf() }
listeners.add(eventListener)
}
}
val bus = MyBus(
listOf(
MyEvent::class.java to MyEventListener()
)
)
class MyEvent : Event
class AnotherEvent : Event
class MyEventListener : EventListener<MyEvent> {
override fun handle(event: MyEvent) {
}
}
发生的情况是,当我尝试使用接受对列表的构造函数创建 MyBus 时,我得到
Type inference failed. Expected type mismatch: inferred type is List<Pair<Class<MyEvent>,MyEventListener>> but List<Pair<Class<Event>,EventListener<Event>>> was expected
但是,如果我将构造函数更改为类似
constructor(listeners: List<Pair<Class<out Event>, EventListener<out Event>>>) : this() {
listeners.forEach {
registerListener(it.first, it.second)
}
}
几乎到处添加,然后 MyBus 构造函数起作用,但是对 registerListener(..) 的调用由于与以前相同的确切原因而中断。所以解决这个问题的唯一方法是在 registerListener 函数上添加“out”。
最佳答案
如果你想要你的 EventListener
能够消费Event
s,那么它的类型必须是不变的或协变的(未声明 out
)。如果它让你通过你的EventListener<MyEvent>
好像它是一个 EventListener<Event>
, 然后你的 MyBus
类(class)可能会调用 listener.handle(event)
上面有一些Event
那不是 MyEvent
,如 AnotherEvent
.然后你会得到一个ClassCastException
当它试图转换这个 AnotherEvent
至MyEvent
.
为了能够存储不同类型的不变事件处理程序,您必须通过使用星形投影来消除方差限制,并在您从 map 中检索它们时进行转换。因此,将映射键变成类对象,而不仅仅是字符串。由于在使用星型投影类型时您不会得到编译器的帮助,因此您需要小心,您只是向 MutableMap 添加一个与与之关联的 Class 键类型相同的项。然后,当您检索项目时,仅转换为不变类型。
您问题的另一部分是您的构造函数需要一个泛型类型。现在它只适用于 Event
所以它不能处理 Event
的子类型. Kotlin(还没有?)不支持构造函数的泛型类型,因此您必须使用工厂函数来执行此操作。
这是上述所有内容的示例。
class MyBus() : EventBus {
private val eventListeners: MutableMap<Class<*>, MutableList<EventListener<*>>> = mutableMapOf()
override fun <E : Event> registerListener(aClass: Class<E>, eventListener: EventListener<E>) {
val listeners = retrieveListeners(aClass)
listeners.add(eventListener)
}
private fun <E: Event> retrieveListeners(aClass: Class<E>): MutableList<EventListener<E>> {
@Suppress("UNCHECKED_CAST")
return eventListeners.getOrPut(aClass) { mutableListOf() } as MutableList<EventListener<E>>
}
}
// Factory function
fun <E : Event> myBusOf(listeners: List<Pair<Class<E>, EventListener<E>>>): MyBus {
return MyBus().apply {
listeners.forEach {
registerListener(it.first, it.second)
}
}
}
您可能希望从
<List>Pair
更改工厂参数的类型。到
vararg Pair
所以更容易使用。
interface EventListener<E : Event> {
fun handle(event: E)
}
Event
的两种实现:
class HelloEvent: Event {
fun sayHello() = println("Hello world")
}
class BoringEvent: Event {}
实现接口(interface)的类:
class HelloEventListener: EventListener<HelloEvent> {
override fun handle(event: HelloEvent) {
event.sayHello()
}
}
现在你有了一个可以处理
的 EventListener仅限
HelloEvent
s。尝试将其视为
EventListener<Event>
:
val eventListener: EventListener<Event> = HelloEventListener() // COMPILE ERROR!
想象一下编译器没有阻止你这样做,你这样做:
val eventListener: EventListener<Event> = HelloEventListener()
eventListener.handle(BoringEvent()) // CLASS CAST EXCEPTION AT RUN TIME!
如果允许,您的 HelloEventListener 将尝试调用
sayHello()
在没有那个功能的 BoringEvent 上,所以它会崩溃。这就是泛型在这里保护您免受伤害的原因。
HelloEventListener.handle()
未调用
event.sayHello()
.那么,它可以安全地处理一个 BoringEvent。但是编译器并没有为您进行这种级别的分析。它只知道你声明了什么,HelloEventListener 不能处理除了 HelloEvent 之外的任何东西。
关于Kotlin 对 "supposedly"正确类型的类型推断,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62881828/
关闭。这个问题需要details or clarity .它目前不接受答案。 想要改进这个问题吗? 通过 editing this post 添加详细信息并澄清问题. 关闭去年。 Improve th
我是 Kotlin 的新手,我正在使用它。我非常想创建一个非常基本的事件总线。所以我想出了这个 interface Event interface EventListener { fun ha
我有这种带有 div 和链接的复杂设置。无论如何,当您将鼠标悬停在我页面左上角的 Logo 时,它应该会发生变化,而且确实如此。但是,如果您的鼠标移动到页面顶部的任何位置(图像上以红色标出),它也会发
我正在尝试使用 cvPerspectiveTransform 来转换四个 2D 点。我已经通过 cvFindHomography 获得了变换矩阵 (3x3)。我不知道要提供什么样的结构才不会遇到错误。
这是一个错误还是有文档表明不要在指令名称中使用后缀“start”?只有“完成”指令有效。 HTML: ... Angular doesn't like the suffix 'start'
我是 jQuery 新手,但只是尝试创建一个可用于过滤表的函数。我已经设置了表格,以便我可以按类选择所有行(效果很好)并在结果上调用each()。在每个()的回调中我有这个 if 语句: if ($(
苹果和橙子的问题。 12 个测试用例中只有 3 个被清除。几个小时后就想不出其他事情了。 示例输入0 7 11 5 15 3 2 -2 2 1 5 -6 示例输出 0 1 1 问题:https://w
我正在尝试在 Netbeans 中正确设置保存时部署(或保存时编译,或任何我应该设置的)。 我正在努力实现这一目标: 1) 如果结构没有改变,需要时热替换一些类(按下按钮,新代码替换旧代码) 2) 在
由 XCode 向导创建的主从项目包含 tableView:cellForRowAtIndexPath: 的实现,它调用 dequeueReusableCellWithIdentifier:forIn
Internet Explorer 9 无法正确显示绝对定位的元素。父级是相对的,所以绝对元素相对于父级div应该是“绝对”的。但是,它的行为更接近“固定”行为。当我尝试在相关元素上触发 hasLay
我对 WM_PAINT 有疑问。基本上我希望在用户 WM_COMMAND 之后调用 WM_PAINT,但由于某种原因它在主函数中仍然被调用。 case WM_PAINT: { cr
我正在尝试来自 Android 应用程序开发傻瓜 的示例,这是一个简单的应用程序,可以切换手机的铃声模式。代码如下。 public class SilentModeToggleActivity ext
根据广为流传的建议,我应该注意让我的大型软件项目尽可能模块化。当然有多种方法可以实现这一点,但我认为没有办法绕过使用更多或更少的接口(interface)类。 以使用 C++ 开发 2D 游戏引擎为例
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 这个问题似乎不是关于 a specific programming problem, a softwar
我在 Rails 3 应用程序中保存哈希时遇到问题。使用控制台时我可以保存它 - 当我通过表单提交哈希时它不起作用。 This SO question addresses它但解决方案对我不起作用。此外
在 CDA R2 CCD 实现指南中,ClinicalDocument 元素(文档的根元素)应该有一个 id 元素。 此 id 元素属于“II”数据类型:http://wiki.hl7.no/inde
我使用g++进行编译时没有任何选项,该程序可在1分钟左右运行。 但是,使用-O3进行编译会使它在大约1-2秒内运行。 我的问题是加快速度是否正常?还是我的代码可能太糟糕了,以至于优化可能会占用很多时间
我有一个基于 HTML 表单的调查问卷,我正试图将其放在一起。当我遇到 float 元素问题时,我已经使用 CCS 排列了所有 HTML 表单元素。 我有一个包含一些表单元素的 DIV,我想将它们放在
以下代码在 Clang 中编译良好并输出 int [3] 数组的大小 #include int main() { const int (&a)[] = { 1, 2, 3 }; std::c
我正在尝试运行以下代码: Cluster cluster = new Cluster(); cluster.add("localhost", port_number); Client client =
我是一名优秀的程序员,十分优秀!