引言

Scala提供的隐式转换和隐式参数功能,是非常有特色的功能。是Java等编程语言所没有的功能。它可以允许你手动指定,将某种类型的对象转换成其他类型的对象。通过这些功能可以实现非常强大而且特殊的功能。

Scala的隐式转换,其实最核心的就是定义隐式转换函数,即implicit conversion function。定义的隐式转换函数,只要在编写的程序内引入,就会被Scala自动使用。在程序中使用到隐式转换函数参数类型定义的对象时,会自动将其传入隐式转换函数,转换为另外一种类型的对象并返回。这就是“隐式转换”。

隐式转换函数叫什么名字是无所谓的,因为通常不会由用户手动调用,而是由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导入隐式转换函数,这样可以缩小隐式转换函数的作用域,避免不需要的隐式转换。

隐式转换触发条件

  1. 调用某个函数,但是给函数传入的参数类型与函数定义的接收类型不匹配。(案例:特殊售票窗口) 
  2. 使用某个类型的对象,调用某个方法,而这个方法并不存在于该类型时。(案例:超人变身) 
  3. 使用某个类型的对象,调用该类中的某个方法,虽然该类型有这个方法,但是给方法传入的参数类型与定义接收类型不匹配(案例:特殊售票窗口加强版)

  
案例:特殊售票窗口加强版


// 定义售票窗口类
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.

Java数组与Scala数组的隐式转换

我们知道在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的隐式转换


// 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]

Scala入门系列(十二):隐式转换的更多相关文章

  1. Spark基础-scala学习(八、隐式转换与隐式参数)

    大纲 隐式转换 使用隐式转换加强现有类型 导入隐式转换函数 隐式转换的发生时机 隐式参数 隐式转换 要实现隐式转换,只要程序可见的范围内定义隐式转换函数即可.Scala会自动使用隐式转换函数.隐式转换 ...

  2. Scala类型参数(泛型)与隐式转换

    package com.yz9 import org.junit.Test import scala.collection.mutable.ListBuffer class test { @Test ...

  3. Scala入门系列(二):条件控制与循环

    条件控制与循环   if表达式 定义:if表达式是有值的,就是if或者else中最后一行语句返回的值. 例如:val isAdult = if (age > 18) 1 else 0 类型推断: ...

  4. Scala学习二十一——隐式转换和隐式参数

    一.本章要点 隐式转换用于类型之间的转换 必须引入隐式转换,并确保它们可以以单个标识符的形式出现在当前作用域 隐式参数列表会要求指定类型的对象.它们可以从当前作用域中以单个标识符定义的隐式对象的获取, ...

  5. Scala学习教程笔记三之函数式编程、集合操作、模式匹配、类型参数、隐式转换、Actor、

    1:Scala和Java的对比: 1.1:Scala中的函数是Java中完全没有的概念.因为Java是完全面向对象的编程语言,没有任何面向过程编程语言的特性,因此Java中的一等公民是类和对象,而且只 ...

  6. Scala之隐式转换implicit详解

    假设我们有一个表示文本的行数的类LineNumber: class LineNumber ( val num : Int ) 我们可以用这个类来表示一本书中每一页的行数: val lineNumOfP ...

  7. 15、Scala隐式转换和隐式参数

    1.隐式转换 2.使用隐式转换加强现有类型 3.隐式转换函数的作用域与导入 4.隐式转换发生时机 5.隐式参数 1.隐式转换 要实现隐式转换,只要程序可见的范围内定义隐式转换函数即可.Scala会自动 ...

  8. 大数据技术之_16_Scala学习_06_面向对象编程-高级+隐式转换和隐式值

    第八章 面向对象编程-高级8.1 静态属性和静态方法8.1.1 静态属性-提出问题8.1.2 基本介绍8.1.3 伴生对象的快速入门8.1.4 伴生对象的小结8.1.5 最佳实践-使用伴生对象解决小孩 ...

  9. Scala入门到精通——第十九节 隐式转换与隐式參数(二)

    作者:摇摆少年梦 配套视频地址:http://www.xuetuwuyou.com/course/12 本节主要内容 隐式參数中的隐式转换 函数中隐式參数使用概要 隐式转换问题梳理 1. 隐式參数中的 ...

随机推荐

  1. 浅谈Jquery中的bind(),live(),delegate(),on()绑定事件方式 [转载]

    前言 因为项目中经常会有利用jquery操作dom元素的增删操作,所以会涉及到dom元素的绑定事件方式,简单的归纳一下bind,live,delegate,on的区别,以便以后查阅,也希望该文章日后能 ...

  2. javascript 关于 this 作用域链

    使用 function f() {}  或者 var f = function() {}  来定义的函数,this 是指向 全局对象   var  a = {    b: 1,    c: funct ...

  3. 虚拟软件vmware安装

    什么是虚拟软件: 虚拟原件是一个可以使你在一台机器上同时运行二个或更多Windows.LINUX等系统.它可以模拟一个标准PC环境.这个环境和真实的计算机一样,都有芯片组.CPU.内存.显卡.声卡.网 ...

  4. 轻松驾驭Tomcat

    Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选.对于一个初学者来说,可以这样 ...

  5. IE浏览器中用Firebug调试网站的方法

    对于大部分做前端设计者而言应该都使用过Firefox浏览器下一款调试网站的扩展插件firebug吧,功能非常的强大,对于我们找出网页兼容性的问题非常的有效.不过对于很多不喜欢使用Firefox浏览器的 ...

  6. 身为运维的你,怎么掌握python才不会失业

    以前,我们都说Python是运维工程师的未来:现在,为什么大家都说不会Python的运维都将失业?运维必须懂开发,特别是python开发,已经形成大家的共识,不懂开发的运维,路会越走越窄. 而现在的情 ...

  7. cursor() — 数据库连接操作 python

    python 操作数据库,要安装一个Python和数据库交互的包MySQL-python-1.2.2.win32-py2.5.exe,然后我们就可以使用MySQLdb这个包进行数据库操作了.      ...

  8. Celery 源码解析五: 远程控制管理

    今天要聊的话题可能被大家关注得不过,但是对于 Celery 来说确实很有用的功能,曾经我在工作中遇到这类情况,就是我们将所有的任务都放在同一个队列里面,然后有一天突然某个同学的代码写得不对,导致大量的 ...

  9. js中的break,continue和return到底怎么用?

    为什么要说个?好像很简单,但是我也会迷糊,不懂有时候为什么要用return,然而break和continue也经常和他放在一起. 所以就一起来说一说,这三个看起来很简单,却常常会出错的关键词的具体用法 ...

  10. [转载] Netty

    转载自http://lippeng.iteye.com/blog/1907279 Netty是什么? 本质:JBoss做的一个Jar包 目的:快速开发高性能.高可靠性的网络服务器和客户端程序 优点:提 ...