本站内容版权属于本人。转载须告知本人,写明出处,并在文首提供指向本站对应文章的链接。
本文链接:Scala学习(九)——隐式转换
本文链接:Scala学习(九)——隐式转换
简介
几乎所有的语言都有隐式转换系统,比如C++中数字类型的隐式转换,js中数字和字符串之间互相隐式转换等等。但在这些语言里,隐式转换都是预定义的,无法给自己定义的类或者对象赋予隐式转换的能力。Scala打破了这种限制,提供了一种方法使得隐式转换不再是预定义类型的专利。
双目运算符的重载问题
前面提到过运算符重载,Scala中运算符本质上是方法。一般说来,运算符前操作数是对象本身,后操作数是输入的参数。当两者类型相同的时候没什么问题,但是如果类型不同呢?且看下面的例子:
1 2 3 4 5 6 7 8 |
class AClass(var n: Int = 0) { def +(i: Int) = new AClass(n + i) override def toString() = s"AClass: $n" } val a = new AClass(1) val b = a + 3 println(b) // AClass: 4 |
对于+
运算符,一般认为是可以交换的,但是如果这里将val b = a + 3
换成val b = 3 + a
的话,就会编译不通过,因为Int
类型并没有提供+(some: AClass)
这个方法。
在C++中,可以通过友元函数重载运算符来完成这件事,在Scala中,则可以通过隐式转换完成。
隐式转换
隐式转换本质上是一个方法,用implict
关键字限定,输入一个对象,返回另一个不同类型的对象,方法名任意。在上例中加入隐式转换的代码如下:
1 2 3 4 5 6 7 8 9 10 11 |
class AClass(var n: Int = 0) { def +(i: Int) = new AClass(n + i) def +(a: AClass) = new AClass(n + a.n) override def toString() = s"AClass: $n" } implicit def intToAClass(i: Int) = new AClass(i) // 隐式转换方法 val a = new AClass(1) val b = 3 + a // 实际相当于b = intToAClass(3).+(a) println(b) |
隐式转换的作用域
要使用隐式转换的地方必须能够直接访问到隐式转换方法,也就是说,如果上例改成这样的话是无法编译通过的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class AClass(var n: Int = 0) { def +(i: Int) = new AClass(n + i) def +(a: AClass) = new AClass(n + a.n) override def toString() = s"AClass: $n" } object BObj { implicit def intToAClass(i: Int) = new AClass(i) } object AObj extends App { val a = new AClass(1) //val b = 3 + a // 编译不通过 //println(b) } |
不过可以通过使用import
语句导入方法,如import BObj.intToAClass
。这样就可以在这里使用隐式转换了。
总结
隐式转换可以使Scala写出很有趣的代码,然而,所有有趣的功能都是一把双刃剑。不恰当的隐式转换会使代码难于理解,过于频繁地使用隐式转换会降低程序的效率。在使用隐式转换的时候,更是要多多注意。