Scala提供的隐式转换特性能够在效果上给一个类增长一些方法,或者用于接收不一样类型的对象. 然而使用Scala的隐式转换是有必定的限制的,总结以下: implicit关键字只能用来修饰方法、变量(参数)和伴随对象。 隐式转换的方法(变量和伴随对象)在当前范围内才有效。若是隐式转换不在当前范围内定义(好比定义在另外一个类中或包含在某个对象中),那么必须经过import语句将其导。举例 package demo { package util { import java.util.Date import java.text.SimpleDateFormat object DateUtil { class DateWrapper(date: Date) { def format(str: String) = new SimpleDateFormat(str).format(date) } implicit def toDateWrapper(date: Date) = new DateWrapper(date) } } package service { import java.text.SimpleDateFormat import java.util.Date // 注: 必须将object Rest的定义放在class Rest以前,或者显式指出str2Date的返回类型,不然编译不经过 object Rest{ class RichDate(str: String){ def toDate(): Date = new SimpleDateFormat("yyyy-MM-dd").parse(str) } implicit def str2Date(str: String) = new RichDate(str) } class Rest { import util.DateUtil._ import Rest._ // 必须把demo.util包下的伴随对象DateWrapper中的全部成员引进来 def today = new Date().format("yyyy-MM-dd"); // 伴随对象 Rest中定义了一个隐式转换函数 def getDate(str: String): Date = str.toDate(); } } object SC4 { import demo.service.Rest def main(args: Array[String]) ={ println (new Rest().today) println(new Rest().getDate("2011-01-09")) } } } 但有一种状况例外,源类型或目标类型(包括源类型和目标类型的类型变量的类型)的伴随对象中隐式转换函数(或变量,伴随对象)不须要显示导入。 好比 package demo{ object MyTestApp{ def main(args: Array[String]): Unit = { val myTest = new MyTest(); myTest.printInt(4) myTest.printYourTest(myTest) // the source type is of MyTest while the target require YourTest myTest.fuxkTest() // there is no method on MyTest but we can still call it } } class YourTest{ override def toString = "Your Test" def fuxkTest() = print("fuxk Test"); } object YourTest{ implicit def myTest2YourTest = new YourTest } class MyTest{ import MyTest._ def printStr(str: String) = println(str) // you can't do it like `printStr(i)` unless you bring the implicit converter `MyTest.int2String`into scope def printInt(i: Int) = printStr(i) def printYourTest(obj: YourTest) = println(obj) def getYorTest(): YourTest = this; } object MyTest { implicit def int2String(i: Int ): String = i.toString implicit def myTest2YourTest(obj: MyTest): YourTest= new YourTest // implicit val myTest2YourTest = (obj : MyTest) => new YourTest } } 这样规定的好处是,经过限制隐式转换的有效范围,使维护和理解代码变得相对容易. 通常来讲,scala编译器会首先在方法调用处的当前范围内查找隐式转换函数;若是没有找到,会尝试在源类型或目标类型(包括源类型和目标类型的类型变量的类型)的伴随对象中查找转换函数,若是仍是没找到,则拒绝编译。 好比: object ABCDMain extends App { class B class C { override def toString() = "I am C"; def printC(c: C) = println(c); } class D implicit def B2C(b: B) = { println("B2C") new C } implicit def D2C(d: D) = { println("D2C") new C } new D().printC(new B) } 运行上述代码,先调用 D2C转换函数将new D()转换成C类, 而后调用C类的printC方法;但发现传入的参数类型是B类,因而搜索当前范围有无合适的转换函数,发现B2C转换函数符合要求。 又好比: object ABCDMain extends App { class A object A { // implicit def A2C(a: A) = { // println("A.A2C"); // new C // } } class C { override def toString() = "I am C"; def printC(c: C) = println(c); } object C { implicit def A2C(a: A) = { println("C.A2C"); new C } } class D implicit def D2C(d: D) = { println("D2C") new C } new D().printC(new A) } 运行上述代码,先调用 D2C转换函数将new D()转换成C类, 而后调用C类的printC方法, 但发现传入的参数类型是A类。因为当前范围无合适的转换函数,故搜索object A和object C内有无合适的转换函数,最后发现object A内有合适的转换函数。 若是同时在object A和object C内发现合适的转换函数,有可能致使编译错误。 再好比: object ABCDMain extends App { class A object A{ //implicit def MA2MC(ma: M[A]) ={ // new M[C]; //} } class C object C{ implicit def MA2MC(ma: AnyRef) = { new M[C]; } } class D { def printM(m: M[C]) = println("i am M[C]"); } class M[T] object M { implicit def MA2MC(ma: M[A]) = { new M[C]; } } new D().printM(new M[A]) } 运行上述代码,printM须要传入类型为M[C]的参数,因为传入了类型为M[A],又在当前范围内没有合适转换函数, 所以同时在object M,object A和object C内搜索合适的转换函数,若是发现两个或以上合适的转换函数,那么有可能致使编译错误。 源类型和目标类型最多只发生一次隐式转换。举例 class A{ override def toString() = "I am A"; } class B{ override def toString() = "I am B"; } class C{ override def toString() = "I am C"; } implicit def A2B(a: A) = new B implicit def B2C(b: B) = new C def printC(c: C) = println(c); printC(new B); // 会调用B2C函数进行隐式转换 printC(new A); // 对 new A 不能连续作两次隐式转换 执行printC(new A)时会报类型不匹配的错 注意:这里所说的”最多只发生一次隐式转换“的意思是,源类型最多只通过一次函数转换变成目标类型(或与目标类型兼容的类型),但在一次方法调用中,可能发生屡次源类型和目标类型之间的转换。 好比 class C { override def toString() = "I am C"; def printC(c: C) = println(c); } class D implicit def B2C (b: B) = { println("B2C"); new C } implicit def D2C(d: D) = { println("D2C"); new C } new D().printC(new B); // 这里会发生两次隐式转换, 一次是D2C, 一次是B2C 另外,当函数定义了隐式参数时,也可能发生屡次隐式转换: object Main extends App { class PrintOps() { def print(implicit i: Int) = println(i); } implicit def user2PrintOps(s: String) = { println("use2PrintOps") new PrintOps } implicit def str2int(implicit s: String, implicit l: List[Int]): Int = { println("str2int") Integer.parseInt(s) } implicit def getString = { println("getString") "123" } implicit def newList = { println("newList") List(2) } "a".print } 运行上述代码,首先调用user2PrintOps函数将"a"转换成PrintOps, 而后调用print方法。因为调用print时没有显式提供implicit参数,所以尝试在当前范围内搜索合适的implicit转换值。编译器发现str2int这个隐式转换函数能提供print所需int类型,但str2int又须要一个隐式的String和一个隐式的List[Int]。编译器继续在当前范围内搜索,最后发现getString和newList这两个隐式转换函数能分别提供一个String实例和一个List[Int]实例。至此编译器知道要先调用getString和newList,再调用str2int,最后调用print。 若是当前的类型匹配或类型兼容,则不会进行隐式转换。举例: class AA extends A{ override def toString() = "I am AA which inherits from A" } implicit def AA2A(aa: AA) = { println("AA --> A"); new A } def printA(a: A) = println(a); printA(new AA) // 由于AA是A的子类型,因此能够把AA类型的值传递给A类型的变量 若是当前范围内有两个或以上合适的隐式转换函数,Scala会怎么处理呢? 在Scala 2.7以及以前的版本中,编译器会发出错误。这跟重载的状况是同样的。好比有两个重载方法foo,一个接收String参数,另外一个接收AnyRef参数,当foo(null) 这样写的时候,编译器会拒绝编译。但在Scala 2.8中,编译器会选择foo(String)这个重载方法,即编译器会选择一个更具体的方法。一样当碰到两个或两个以上合适的隐式转换函数时,编译器也会选择一个更具体的方法。至于哪一个方法被认为更具体,能够根据如下的规则进行判断: a) 前者的函数参数类型是后者的子类型,则前者更具体 b) 假设两个隐式转换函数都定义在一个类中,若是前者所在的类是后者的子类,那么前者更具体 因为隐式转换会给代码带来“魔幻”效果,对于不熟悉这种特性的人会感受难受。 笔者曾经发现一个利用隐式转换改变方法执行顺序的例子: // TernaryOp对象提供了相似java的三目运算符号操做 object TernaryOp { class Ternary[T](t: T) { println("Ternary") def is[R](bte: BranchThenElse[T,R]) = { println("is ... ") if (bte.branch(t)) bte.then(t) else bte.elze(t) } } class Branch[T](branch: T => Boolean) { println("branch"); def ?[R] (then: T => R) = new BranchThen(branch,then) } class BranchThen[T,R](val branch: T => Boolean, val then: T => R){ println("BranchThen") } class Elze[T,R](elze: T => R) { println("Elze") def :: (bt: BranchThen[T,R]) = new BranchThenElse(bt.branch,bt.then,elze) } class BranchThenElse[T,R](val branch: T => Boolean, val then: T => R, val elze: T => R) implicit def any2Ternary[T](t: T) = new Ternary(t) implicit def fct2Branch[T](branch: T => Boolean) = new Branch(branch) implicit def fct2Elze[T,R](elze: T => R) = new Elze(elze) def test = { this.getClass.getSimpleName is {s: String => s.endsWith("$")} ? {s: String => s.init} :: {s: String => s} } } TernaryOp.test // 思考一下test中会发生怎样转换?若搞不明白,请参考<a href="https://gist.github.com/1388106" target="_blank" rel="nofollow">https://gist.github.com/1388106</a> 笔者以为,如不必尽可能少用隐式转换,毕竟太“魔幻”的代码理解起来仍是要费点劲。 BTW: 命名函数的参数能够声明为implicit,但implicit必须出如今首位,而且是对全部的参数有效,不能只给某些参数声明为implicit,好比: def maxFunc(implicit i1: Int, i2: Int) = i1 + i2 maxFunc带有两个implicit参数i1和i2。你没法只声明一个implicit参数。你不能这样写 def maxFunc( i1: Int, implicit i2: Int) = i1 + i2 也不能这样写: def maxFunc(implicit i1: Int, implicit i2: Int) = i1 + i2 若是你只想声明一个implicit,使用curry,如 def maxFunc(implicit i1: Int)(i2: Int) = i1 + i2 2. 匿名函数不能声明隐式参数,即不能这样写: val f = (implicit s: String) => s+1 3. 若是一个函数带有implicit参数,则没法经过 _ 获得该函数引用。你尝试这样作是没法编译的: def maxFunc(implicit i1: Int, i2: Int) = i1 + i2 val f = maxFunc _ // 编译错误 4. 能够给匿名函数的参数加上implicit,好比: def h( implicit s: String) = println("here : "+s) def g(func: String => Int) = { println(func("a")) } g{ implicit s => h; 2 } 这里的implicit s => h; 2至关于 s => implicit val xx = s; h; 2. 若是匿名函数有两个参数,貌似不能给参数加上implicit