代码之家  ›  专栏  ›  技术社区  ›  pathikrit

如果def名称为tostring,则scala隐式def不起作用

  •  2
  • pathikrit  · 技术社区  · 6 年前

    此代码无法编译:

    object Foo {
      implicit def toString(i: Int): String = i.toString      
      def foo(x: String) = println(x)
      foo(23)
    }    
    

    以上代码编译失败,错误如下:

    error: type mismatch;
    found   : scala.this.Int(23)
    required: String
      foo(23)
    

    但是,这段代码编译

    object Foo {
      implicit def asString(i: Int): String = i.toString      
      def foo(x: String) = println(x)
      foo(23)
    }
    

    为什么 implicit def 应该重要吗?

    注: 如果方法名为 equals ,它也不起作用,但如果它被命名为 hashCode clone 等。

    0 回复  |  直到 6 年前
        1
  •  12
  •   Travis Brown    6 年前

    问题不在于这个 toString 已超载 Foo ,正如另一个(现在已删除)答案所说(您可以尝试重载 asString 同样,它也会起作用 弦线 你输入的碰撞 弦线 封闭类(在您的例子中是由repl组成的合成对象)。

    我认为下面的隐式自由示例(也不使用诸如 弦线 )更清楚地说明问题:

    class Foo {
      def asString(i: Int): String = "this is the one from Foo!"
    }
    
    class Bar {
      def asString(i: Int): String = "this is the one from Bar!"
    }
    
    object Demo extends Bar {
      val instance = new Foo
      import instance._
    
      println(asString(23))
    }
    

    这将使用 字符串 Bar ,即使您可能认为导入的优先:

    scala> Demo
    this is the one from Bar!
    res1: Demo.type = Demo$@6987a133
    

    实际上它将使用 酒吧 即使争论不一致:

    class Foo {
      def asString(i: Int): String = "this is the one from Foo!"
    }
    
    class Bar {
      def asString(): String = "this is the one from Bar!"
    }
    
    object Demo extends Bar {
      val instance = new Foo
      import instance._
    
      println(asString(23))
    }
    

    无法编译:

    <pastie>:25: error: no arguments allowed for nullary method asString: ()String
      println(asString(324))
                       ^
    

    现在我们可以让它看起来更像您的原始代码:

    class Foo {
      implicit def asString(i: Int): String = "this is the one from Foo!"
      def foo(s: String): String = s
    }
    
    class Bar {
      def asString(): String = "this is the one from Bar!"
    }
    
    object Demo extends Bar {
      val instance = new Foo
      import instance._
    
      println(foo(23))
    }
    

    失败的原因与您看到的错误相同:导入的隐式转换被封闭类中同名的定义隐藏。

    脚注1

    你问了以下问题:

    为什么 implicit def 应该重要吗?

    隐含物名称 总是 . 这就是语言的工作方式。例如:

    scala> List(1, 2, 3) + ""
    res0: String = List(1, 2, 3)
    
    scala> trait Garbage
    defined trait Garbage
    
    scala> implicit val any2stringadd: Garbage = new Garbage {}
    any2stringadd: Garbage = $anon$1@5b000fe6
    
    scala> List(1, 2, 3) + ""
    <console>:13: error: value + is not a member of List[Int]
           List(1, 2, 3) + ""
                         ^
    

    我们所做的是定义一个隐式值来隐藏 any2stringadd 隐式转换 scala.Predef . (是的,这有点可怕。)

    脚注2

    我认为这里可能存在编译器错误,至少就错误消息而言。如果你在我上面的第二个版本中稍微改变一下,例如:

    class Foo {
      def asString(i: Int): String = "this is the one from Foo!"
    }
    
    class Bar {
      def asString(): String = "this is the one from Bar!"
    }
    
    object Demo extends Bar {
      def test(): Unit = {
        val instance = new Foo
        import instance._
    
        println(asString(23))
      }
    }
    

    你会得到一个更合理的信息:

    <pastie>:26: error: reference to asString is ambiguous;
    it is both defined in class Bar and imported subsequently by
    import instance._
        println(asString(23))
                ^
    

    在我看来,这几乎肯定是编译器 应该 告诉你你原来的案子。我也不知道为什么隐式转换会被考虑,但是它是,正如您可以知道的,如果您在repl中运行代码 -Xlog-implicits :

    scala> foo(23)
    <console>:16: toString is not a valid implicit value for Int(23) => String because:
    no arguments allowed for nullary method toString: ()String
           foo(23)
               ^
    

    所以看起来含蓄在另一个上面消失了 弦线 ?老实说,我不知道这里发生了什么,但我有90%的把握这是个错误。

        2
  •  2
  •   Luis Miguel Mejía Suárez    6 年前

    不知道这算不算答案 (也许对编译器内部有更多了解的人可以给出更详细的解释) ,但在对您的代码进行了一段时间的分析后,我发现了一些问题,我认为这是错误的根源。

    鉴于:

    object Foo {
      implicit def toString(i: Int): String = i.toString      
    }
    
    import Foo.toString
    

    然后:

    val s: String = 10
    

    生产:

    :10:警告:导入的“toString”被类对象中toString方法的定义永久隐藏
    导入foo.tostring

    我认为这意味着,隐式转换是隐藏的,因为它的名称与universal冲突 toString 方法定义于 java.langObject (和) scala.Any ) .


    够古董了,这行。

    implicit val int2str: Int => String = Foo.toString
    val s: String = 10
    // s: String = 10