【问题标题】:Scala wrong implicit ambiguityScala错误的隐含歧义
【发布时间】:2013-07-12 00:53:47
【问题描述】:

使用 Scala 2.10 编译失败

object TestImplicits {
  class View
  implicit class RichView[A <: View](v: A) {
    def onClicked() = println(v + " was clicked")
  }
  implicit def intToView[A <: View](i: Int): A = ???
  implicit def intToRichView(id: Int): RichView[View] = ???
  1.onClicked()
}

错误如下:

<console>:20: error: type mismatch;
 found   : Int(1)
 required: ?{def onClickedd: ?}
Note that implicit conversions are not applicable because they are ambiguous:
 both method intToView in object TestImplicits of type [A <: TestImplicits.View]
(i: Int)A
 and method intToRichView in object TestImplicits of type (id: Int)TestImplicits
.RichView[TestImplicits.View]
 are possible conversion functions from Int(1) to ?{def onClickedd: ?}
                  1.onClickedd()
                  ^

但是等等,View 类没有方法 onClicked ! 这就是我遇到问题的原因:

  • 我无法删除第一个隐式函数,因为我使用它来实现我的第二个函数。
  • 如果我删除第二个隐式函数,我将失去电源功能。

我怎样才能同时保留两者?

更新

如果我将1.onClicked() 更改为intToView(1).onClicked(),我会收到以下有趣的错误:

<console>:20: error: value onClicked is not a member of Nothing
                  intToView(1).onClicked()
                               ^

如果有人能向我解释一下,我将不胜感激。

【问题讨论】:

    标签: scala implicit type-mismatch


    【解决方案1】:

    真正的问题是您的intToView 方法根本没有意义,也无法有效实现。

    现在它实际上是在说“给我一个整数并告诉我View 的子类型,我会给你一个该子类型的实例”。但这是不可能的,因为我可以发明一些非常混乱的类来扩展View。例如:

    class NamedView(val name: String) extends View
    

    intToView 方法的类型签名保证以下内容应该有效:

    val viewName: String = intToView[NamedView](13).name
    

    这可能是什么?你怎么能实现intToView,让它知道如何创建一个NamedView,这是我当场编造的?

    所以??? 允许您编写一个可以编译但没有意义的方法。这是一种危险的情况,因为这意味着编译器可能会做各种愚蠢的事情。

    在这种情况下,它所做的愚蠢的事情是决定它在这里真正拥有的是从IntNothing 的隐式转换,当然NothingRichView[_] 的子类型,所以它也有从IntRichView[_] 的隐式转换。然后你给它另一个从IntRichView[_]的隐式转换,它就会窒息。

    这种情况下最简单的解决方案是把intToView中的类型参数去掉:

    implicit def intToView(i: Int): View = ???
    

    这是否适用于您的实际用例取决于您正在尝试做什么。

    【讨论】:

    • 好吧,我真的很想把它隐藏起来,但这是我对 intToView 的实现:implicit def intToView[A &lt;: View](id: Int): A = v.findViewById(id).asInstanceOf[A] 基本上它允许我在 Android 中说 (my_id : TextView).setText(...) 以便它工作。所以我的方法确实有意义。
    • 那么基本上,您说Nothing 是一种实现所有可能方法的类型?
    • 将这种类型的转换与隐式转换混合起来似乎是一个非常糟糕的主意。
    【解决方案2】:

    这是我的解决方案,涵盖诸如 TextView 等情况:我从通用函数中删除了隐式函数,并显式添加了隐式函数以将 id 转换为视图。这样,我可以拥有我想要的所有方面。

    object TestImplicits {
      class View
      class TextView extends View { def setText(s: String): Unit = println(s"text set! $s"}
      implicit class RichView(v: View) {
        def onClicked() = println(s"$v was clicked")
      }
      def intToView[A <: View](id: Int): A = findViewById(id).asInstanceOf[A]
      implicit def intToTextView(id: Int): TextView = intToView[TextView](id)
      implicit def intToRichView(id: Int): RichView[View] = new RichView(intToView[View](id))
      1.onClicked()
      2.setText("my text")
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-02
      • 1970-01-01
      • 2012-01-29
      • 1970-01-01
      相关资源
      最近更新 更多