Scala 提供的隐式转换和隐式参数功能,是非常有特色的功能。是 Java 等编程语言所没有的功能。它可以允许你手动指定,将某种类型的对象转换成其他类型的对象。通过这些功能可以实现非常强大而且特殊的功能。
Scala 的隐式转换,其实最核心的就是定义隐式转换函数,即
。定义的隐式转换函数,只要在编写的程序内引入,就会被 Scala 自动使用。在程序中使用到隐式转换函数参数类型定义的对象时,会自动将其传入隐式转换函数,转换为另外一种类型的对象并返回。这就是 "隐式转换"。
- implicit conversion function
隐式转换函数叫什么名字是无所谓的,因为通常不会由用户手动调用,而是由 Scala 进行调用。但是如果要使用隐式转换,则需要对隐式转换函数进行导入。因此通常建议将隐式转换函数的名称命名为 "one2one" 的形式。
要实现隐式转换,只要程序可见的范围内定义隐式转换函数即可。Scala 会自动调用隐式转换函数。
隐式转换函数与普通函数唯一的区别就是要以 implicit 开头,而且最好要定义函数返回类型。
案例:特殊售票窗口(只接受特殊人群:比如学生、老人等)
- class
- SpecialPerson
- (
- val name: String
- )
- class
- Student
- (
- val name: String
- )
- class
- Older
- (
- val name: String
- )
- // 我们想通过隐式转化把学生和老人转化为特殊人群,这样他们就可以在特殊窗口买票。
- implicit
- def object2SpecialPerson
- (obj:
- Object
- ):
- SpecialPerson
- = {
- if(obj.getClass == classOf[Student]){ val stu = obj.asInstanceOf[Student]; new SpecialPerson(stu.name) }
- else if (obj.getClass == classOf[Older]) { val older = obj.asInstanceOf[Older]; new SpecialPerson(
- older.name) }
- else Nil
- }
- var ticketNumber = 0
- // 只接受特殊人群的买票服务
- def buySpecialTicket
- (p:
- SpecialPerson
- ) = {
- ticketNumber += 1
- "T-" + ticketNumber
- }
- // 测试
- scala>
- val
- stu =
- new
- Student
- (
- "sparks"
- )
- stu: Student = Student@388623ad
- scala>
- val
- old =
- new
- Older
- (
- "leo"
- )
- old: Older = Older@3453acd2
- // 学生和老人通过隐式转换后均可买特殊票
- scala> buySpecialTicket(stu)
- res9: String = T-1
- scala> buySpecialTicket(old)
- res10: String = T-2
隐式转换非常强大的一个功能,就是可以在不知不觉中加强现有类型的功能。也就是说,可以为某个类定义一个加强版的类,并定义互相之间的隐式转换函数,从而让源类在使用加强版类的方法时,由 Scala 自动进行隐式转换,然后再调用加强类中特有的方法。
案例:超人变身
- class
- Man
- (
- val name: String
- )
- // 定义超人类,具有发射激光方法
- class
- Superman
- (
- val name: String
- )
- {
- def emitLaser
- = println(
- "emit a laster!"
- )
- }
- // 定义隐式转换函数,将普通人转为超人
- implicit
- def man2superman
- (man:
- Man
- ):
- Superman
- =
- new
- Superman
- (man.name)
- defined class Man
- defined class Superman
- man2superman: (man: Man)Superman
- // 创建普通人类对象sparks
- scala>
- val
- sparks =
- new
- Man
- (
- "leo"
- )
- sparks: Man = Man@75c56eb9
- // sparks通过隐式转换变身超人,拥有发射激光方法
- scala> sparks.emitLaser
- emit a laster!
Scala 默认会使用两种隐式转换,一种是源类型或目标类型的伴生对象内的的隐式转化函数;一种是当前程序作用域内的可以用唯一标识符表示的隐式转换函数。(上面两个例子都属于第二种情况)
如果隐式转换函数不在上述两种情况下的话,那么就必须手动使用 import 语法导入,例如:import test._ 。通常建议,仅仅在需要进行隐式转换的地方,比如某个函数或者方法内,用 import 导入隐式转换函数,这样可以缩小隐式转换函数的作用域,避免不需要的隐式转换。
案例:特殊售票窗口加强版
- // 定义售票窗口类
- class TicketHouse {
- var ticketNumber = 0
- def buySpecialTicket
- (p:
- SpecialPerson
- ) = {
- ticketNumber += 1
- "T-" + ticketNumber
- }
- }
- defined class TicketHouse
- // 测试
- scala> val ticketHouse = new TicketHouse
- ticketHouse: TicketHouse = TicketHouse@522b2631
- scala>
- val
- spark =
- new
- Student
- (
- "sparks"
- )
- spark: Student = Student@588ffeb
- // 当传入一个学生对象时,触发第三种隐式转换条件
- scala> ticketHouse.buySpecialTicket(spark)
- res2: String = T-1
隐式参数指的是在函数或者方法中,定义一个用 implicit 修饰的参数,此时 Scala 会尝试找到一个指定类型的,用 implicit 修饰的对象即隐式值,如果找到就将其注入参数。
Scala 会在两个范围内查找:一种是当前作用域内可见的 val 或 var 定义的隐式变量;一种是隐式参数类型的伴生对象内的隐式值。
案例:考试签到
- class SignPen{
- def write
- (content:
- String
- ) =println(content)
- }
- // 定义隐式参数
- implicit
- val
- signPen =
- new
- SignPen
- // 函数中使用隐式参数,此时scala会尝试找到一个指定SignPen类型的用implicit修饰的对象
- def signForExam
- (name:
- String
- ) (
- implicit
- signPen:
- SignPen
- ) {
- signPen.write(name + " come to exam in time.")
- }
- defined class SignPen
- signPen: SignPen = SignPen@773dab28
- signForExam: (name:
- String
- )(
- implicit
- signPen:
- SignPen
- )
- Unit
- // 测试:当同学sparks来签到时,函数调用隐式参数的值完成签到
- scala> signForExam("sparks")
- sparks come to exam in time.
我们知道在 Scala 代码中,是直接可以调用 JDK(JAVA)API 的,比如调用一个 Java 数组类的方法,势必可能会传入 Java 类型的 ArrayList,但是 Scala 中构造出来的其实是 ArrayBuffer,你直接把 Scala 的 ArrayBuffer 传入 Java 接收 ArrayList 的方法,肯定不行。所以这时候隐式转换就派上用场了!
- // 导入隐式转换函数,将scala的buffer类型转换为javalist类型
- import scala.collection.JavaConversions.bufferAsJavaList
- import scala.collection.mutable.ArrayBuffer
- // 测试
- val command = ArrayBuffer("javac", "C:\\Users\\Administrator\\Desktop\\Hello.java")
- val processBuilder = new ProcessBuilder(command)
- val process = processBuilder.start()
- // 转换成功
- val res = process.waitFor()
- res: Int = 2
- // 导入隐式转换函数,将javalist类型转换为scala的buffer类型
- import scala.collection.JavaConversions.asScalaBuffer
- import scala.collection.mutable.Buffer
- // 转换成功
- scala> val cmd: Buffer[String] = processBuilder.command()
- cmd: scala.collection.mutable.Buffer[String] = ArrayBuffer(javac, C:\Users\Administrator\Desktop\Hel
- lo.java)
- // Java Map转换为Scala Map
- import scala.collection.JavaConversions.mapAsScalaMap
- // 创建Java.util.HashMap对象
- val
- javaScores =
- new
- java.util.
- HashMap
- [
- String
- ,
- Int
- ]()
- javaScores.put("Alice", 10)
- javaScores.put("Bob", 3)
- javaScores.put("Cindy", 8)
- // 转换成功
- val scalaScorces: scala.collection.mutable.Map[String, Int] = javaScores
- scalaScorces: scala.collection.mutable.Map[String,Int] = Map(Bob -> 3, Alice -> 10, Cindy -> 8)
- // Scala Map转换为Java Map
- import scala.collection.JavaConversions.mapAsJavaMap
- import java.awt.font.TextAttribute._
- scala>
- val
- scalaAttrMap =
- Map
- (
- FAMILY
- ->
- "Sermi"
- ,
- SIZE
- ->
- 12
- )
- scalaAttrMap: scala.collection.immutable.Map[java.awt.font.TextAttribute,Any] = Map(java.awt.font.Te
- xtAttribute(family) -> Sermi, java.awt.font.TextAttribute(size) -> 12)
- // 转换成功
- scala> val font = new java.awt.Font(scalaAttrMap)
- font: java.awt.Font = java.awt.Font[family=Dialog,name=Sermi,style=plain,size=12]
来源: http://www.cnblogs.com/LiCheng-/p/8076090.html