隐式转换,是否需要导入?

14

我写

object MyString {
  implicit def stringToMyString(s: String) = new MyString(s)    
}

class MyString(str: String) {
  def camelize = str.split("_").map(_.capitalize).mkString

  override def toString = str
}


object Parse {
  def main(args: Array[String]) {
    val x = "active_record".camelize
    // ...
  }
}

在我的程序中。这会导致编译错误。在我插入代码之后

  import MyString.stringToMyString

然后它就能正常工作了。

根据奥德斯基在《Scala编程》中的说法,源类型或目标类型的伴生对象中的隐式转换不需要被导入。

3个回答

16

源类型或目标类型的伴生对象中的隐式转换不需要被导入。

确实如此。现在,方法camelize在类MyString上进行了定义,并且,在其对象伴生对象中确实存在对MyString的隐式转换。然而,在代码中没有任何内容告诉编译器MyString是预期的目标类型。

相反,如果您写成这样:

val x = ("active_record": MyString).camelize

如果你这样做,它会起作用,因为编译器会知道你期望"active_record"是一个MyString,使其在对象MyString中查找隐式转换。

这可能看起来有些受限制,但实际上在许多地方都可以使用。比如说,你有:

class Fraction(num: Int, denom: Int) {
    ...
    def +(b: Fraction) = ...
    ...
}

然后你有一段像这样的代码:

val x: Fraction = ...
val y = x + 5

现在,x确实有一个+方法,其期望类型Fraction。所以编译器会在这里查找从IntFraction的隐式转换,位于对象Fraction中(如果有一个对象Int,则也在其中,因为那是源类型)。


13
在这种情况下,你需要使用 import ,因为编译器不知道你从哪里引用了 camelize 方法。如果类型是明确的,则可以在没有 import 的情况下编译:
object Parse {
  def foo(s: MyString) = s.camelize

  def main(args: Array[String]) {
    val x = foo("active_record")
    println(x.toString)
  }
}

参见Pimp my library pattern,该模式基于Martin的文章:

请注意,不能在顶层放置def,因此无法定义具有全局范围的隐式转换。 解决方案是将def放在对象内部,然后导入它,即

object Implicits {
    implicit def listExtensions[A](xs : List[A]) = new ListExtensions(xs)
}

然后在每个源文件的顶部,与其他导入项一起:

import Implicits._

1
如果我没记错的话,你可以将import放入包对象中,这样它至少会有点“全局” :-) - Landei

0

我在《Scala编程》一书中尝试了Rational类的示例,在其伴生对象中放置了一个隐式方法:

object Rational {
  implicit def intToRational(num: Int) = 
    new Rational(num)
}

但是代码

2 + new Rational(1, 2)

不起作用。为了进行转换,需要遵循单一标识符规则,即使它在伴生对象中定义,也需要将显式方法导入到作用域中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接