Scala的类型推断如何与类型边界一起使用?

当类型参数存在类型边界时,Scala如何确定要推断的类型?例如:

def onMouseClicked_=[T >: MouseEvent](lambda: T => Unit) = 
  setOnMouseClicked(new EventHandler[T] {
    override def handle(event: T): Unit = lambda(event)
  })

尝试使用此功能时,例如:

onMouseClicked = { me => doSomething() }

我将有推断类型的MouseEvent. T的类型绑定是一个较低的类型绑定,因此T必须是MouseEvent类型或MouseEvent的超类型,那么为什么我有推断类型的MouseEvent?它不应该推断出最普​​遍的类型吗?

是不是我不了解Scala的类型推断是如何工作的?或者我对类型界限的理解是完全错误的?

编辑:

假设我们进一步将T的类型限制为Event的子类型,其中Event是MouseEvent的超类型.所以我们得到:

def onMouseClicked_=[T >: MouseEvent <: Event](lambda: T => Unit) = 
  setOnMouseClicked(new EventHandler[T] {
    override def handle(event: T): Unit = lambda(event)
  })

所以如果我们这样做

onMouseClicked = { me: MouseDragEvent => doSomething() }

其中MouseDragEvent是MouseEvent的子类型,编译因类型错误而失败,正如预期的那样,因为绑定确保我必须是MouseEvent的超类型.

然而,如果我们这样做

onMouseClicked = { me: Any => doSomething() }

编译成功.很明显,Any不是Event的子类型,为什么编译成功呢?什么是T的推断类型?

解决方法

我会试一试,但我不确定一切都是超级正确的还是反正清楚.

前提

要解决的第一点是从类型T到结果R(T => R)的函数在其返回类型中是协变的并且在其参数中是逆变的:即函数[-T,R].

为了使它变短,意味着函数fsub是函数f的子类型,其返回类型必须是R(协变量)的相同类型或子类型,并且其参数必须相同或者是T的超类型(逆变量).

让我们试着理解这个:如果你想能够使用fsub,其中af是预期的(参见Liskov),你需要一个fsub,它最多可以处理一个T的传递参数,但没有更具体的,因为那就是f的呼叫者预计会传递给它.此外,由于将传递T,您可以更加放松fsub的参数并处理其中一种超类型,因为传递给它的每个T也是其超类型的实例.

这就是当我们说函数的参数在其类型中是逆变时的意思:它告诉你它的类型如何在关系中对函数进行整体改变,或实际上反过来.

鉴于此,让我们回到具体案例.处理程序的mutator(onMouseClicked_ =)应该至少接受一个类型为MouseEvent =>的lambda.单元.让我们给它一个名字,处理程序:MouseEvent =>单元.

但它并没有在这里结束,你应该期望能够传递这个lambda的子类型的方法,这个“子函数”的类型是什么?正如我们之前所说,它可以接受MouseEvent或它的任何超类型,然后是subhandler:T =>单位为T:>的MouseEvent.

而这正是您所看到的方法的一般形式.

结论

我希望现在很清楚,该方法将T定义为MouseEvent的(非严格)超类型的原因并不是因为你的处理程序将收到任何与MouseEvent不同的东西,而是因为你可以传递一个能够传递给它的lambda只处理更抽象类型的事件……(例如,你可以使用我:Any => doSomething())

相关文章

共收录Twitter的14款开源软件,第1页Twitter的Emoji表情 Tw...
Java和Scala中关于==的区别Java:==比较两个变量本身的值,即...
本篇内容主要讲解“Scala怎么使用”,感兴趣的朋友不妨来看看...
这篇文章主要介绍“Scala是一种什么语言”,在日常操作中,相...
这篇文章主要介绍“Scala Trait怎么使用”,在日常操作中,相...
这篇文章主要介绍“Scala类型检查与模式匹配怎么使用”,在日...