不废话,先上例子:定义一个参数类型为String的函数:

scala> def sayHello(name:String) = println("hello " + name)
sayHello: (name: String)Unit

然后调用此函数,但传入参数为1,可想而知一定会报错:

scala> sayHello(1)
<console>:13: error: type mismatch;
found : Int(1)
required: String
sayHello(1)

很简单,因为函数要求的参数类型为String,而传入类型为Int,类型不匹配。但是,如果此时定义一个隐式类型转换函数,指明原参数类型和转换后的参数类型,函数名随意╮(╯▽╰)╭:

scala> implicit def hahahaa(s:Int):String = s.toString
<console>:11: warning: implicit conversion method hahahaa should be enabled
by making the implicit value scala.language.implicitConversions visible.
This can be achieved by adding the import clause 'import scala.language.implicitConversions'
or by setting the compiler option -language:implicitConversions.
See the Scaladoc for value scala.language.implicitConversions for a discussion
why the feature should be explicitly enabled.
implicit def hahahaa(s:Int):String = s.toString

虽然报了一大堆警告,但是可以看出来定义成功了。(警告可以通过import scala.language.implicitConversions来消除)

此时再调用

scala> sayHello(1)
hello 1

可以看到调用成功,事实上此类函数时,一般会先根据传入参数类型和要求参数类型寻找相应的隐式转换函数,将转换函数的执行结果作为输入。

回归正题,scala的隐式转换有3中应用场景:

1. 即上边的参数转换场景:当传入的参数与定义的参数类型不匹配,则在当前作用域内寻找对应的隐式转换函数;

2. 参数值隐式传入场景:当函数中有定义隐式参数时(需单独列出来),会在当前作用域寻找同类型的隐式变量/标量,直接传入,无需在调用时传入:

scala> def sayHi(name:String)(implicit words:String) = print("Hi,"+name+"!"+words)
sayHi: (name: String)(implicit words: String)Unit scala> implicit val w = "nice to meet you!"
w: String = nice to meet you!
scala> sayHi("zhangsan")
Hi,zhangsan!nice to meet you!

其实相当于定制化的函数内变量初始化。需要注意,在这里不能有两个相同类型的隐式值,否则出错。

3. 增强功能型隐式转换:很容易理解,在A类对象上调用B类对象独有方法,肯定报错;但是如果定义了从A类到B类的隐式转换函数,那么久能行通了,先通过转换函数转换后再调用:

ImplicitTest.scala:

package basic{
package test{
class Dog(n:String){
val name = n
def bark = print("wang,wang,wang...")
val dogNum = System.nanoTime
}
class Person(n:String){
val name = n
def talk = print("Hello, nice to meet you.")
}
}
}

run.scala

import basic.test._

var liuqing = new Person("Liuqing")
implicit def doBadThings(p:Person) = {
new Dog(p.name + "_dog")
}
liuqing.bark

执行结果:

D:\workspace\scala\test>scala run.scala
warning: there was one feature warning; re-run with -feature for details
one warning found
wang,wang,wang...

你看,柳青可以学狗叫了^^

但是要注意,这里的隐式转换只在方法调用时能触发,在public成员访问时不能触发,即,这里调用liuqing.dogNum,并不会触发转换,会报错。

隐式转换可以说是scala中的“核武器”,在许多高级编程里都会用到,而且十分实用!下面我们以scala sdk源码为例进行实战分析。

我们知道,在scala中java的容器类是可以直接导入后使用的,然而scala中其实也有与java对应的容器类,而且java的容器类其实有一种便捷的方法转换为scala中对应的容器类:

出自spark:core

import scala.collection.JavaConverters._ //这一句十分重要,没有这个导入就不能用asScala了!!

class SparkConf(loadDefaults: Boolean) extends Cloneable with Logging with Serializable {

 import SparkConf._

private val settings = new ConcurrentHashMap[String, String]()//这里的setting是java.util.concurrent.concurrentMap
...
override def clone: SparkConf = {
val cloned = new SparkConf(false)
settings.entrySet().asScala.foreach { e => //这里通过asScala瞬间将java.util.HashSet装换为scala.collection.mutable.Set
cloned.set(e.getKey(), e.getValue(), true)
}
cloned
}
...

这里用到的是scala自带的将java HashSet转换为scala mutable Set的用法,那么这个神奇的功能究竟是怎么实现的呢?去看看scala sdk的源码:

首先看看JavaConverters究竟是何方神圣

package scala
package collection import convert._ object JavaConverters extends DecorateAsJava with DecorateAsScala

去掉注释其实内容就这么点。。。是不是傻眼了?继续往父类看:

package scala
package collection
package convert import java.{ lang => jl, util => ju }, java.util.{ concurrent => juc }
import Decorators._
import scala.language.implicitConversions /** Defines `asScala` extension methods for [[JavaConverters]]. */
trait DecorateAsScala extends AsScalaConverters {
/**
* Adds an `asScala` method that implicitly converts a Java `Iterator` to a Scala `Iterator`.
* @see [[asScalaIterator]]
*/
implicit def asScalaIteratorConverter[A](i : ju.Iterator[A]): AsScala[Iterator[A]] =
new AsScala(asScalaIterator(i))
...
/**
* Adds an `asScala` method that implicitly converts a Java `List` to a Scala mutable `Buffer`.
* @see [[asScalaBuffer]]
*/
implicit def asScalaBufferConverter[A](l : ju.List[A]): AsScala[mutable.Buffer[A]] =
new AsScala(asScalaBuffer(l)) /**
* Adds an `asScala` method that implicitly converts a Java `Set` to a Scala mutable `Set`.
* @see [[asScalaSet]]
*/
implicit def asScalaSetConverter[A](s : ju.Set[A]): AsScala[mutable.Set[A]] =
new AsScala(asScalaSet(s)) /**
* Adds an `asScala` method that implicitly converts a Java `Map` to a Scala mutable `Map`.
* @see [[mapAsScalaMap]]
*/
implicit def mapAsScalaMapConverter[A, B](m : ju.Map[A, B]): AsScala[mutable.Map[A, B]] =
new AsScala(mapAsScalaMap(m))
...
}

从这里我们大概能看出些眉目 :应该是通过隐式转换的方法将java的每种容器类转换统一转换到AsScala类,那么按照这个推想,asScala方法就有可能是AsScala类中定义的。那么再看看AsScala类中有些什么东东吧:

package scala
package collection
package convert import java.{ util => ju } private[collection] object Decorators {
/** Generic class containing the `asJava` converter method */
class AsJava[A](op: => A) {
/** Converts a Scala collection to the corresponding Java collection */
def asJava: A = op
} /** Generic class containing the `asScala` converter method */
class AsScala[A](op: => A) {
/** Converts a Java collection to the corresponding Scala collection */
def asScala: A = op //我们用的asScala不就在这么(*^▽^*)
} /** Generic class containing the `asJavaCollection` converter method */
class AsJavaCollection[A](i: Iterable[A]) {
/** Converts a Scala `Iterable` to a Java `Collection` */
def asJavaCollection: ju.Collection[A] = JavaConverters.asJavaCollection(i)
} /** Generic class containing the `asJavaEnumeration` converter method */
class AsJavaEnumeration[A](i: Iterator[A]) {
/** Converts a Scala `Iterator` to a Java `Enumeration` */
def asJavaEnumeration: ju.Enumeration[A] = JavaConverters.asJavaEnumeration(i)
} /** Generic class containing the `asJavaDictionary` converter method */
class AsJavaDictionary[A, B](m : mutable.Map[A, B]) {
/** Converts a Scala `Map` to a Java `Dictionary` */
def asJavaDictionary: ju.Dictionary[A, B] = JavaConverters.asJavaDictionary(m)
}
}

哈哈,终于找到了,原来你藏在这儿啊!可以看到,这个方法等效于主构造函数传入的参数,而该参数是一个传名调用(关于传名调用和传值调用的区别将在下下节详述),那么再来看看在转换Set时用到的什么样的op吧:

  def asScalaSet[A](s: ju.Set[A]): mutable.Set[A] = s match {
case null => null
case MutableSetWrapper(wrapped) => wrapped
case _ => new JSetWrapper(s)
}

这里逻辑也很简单,做了个match,很显然,我们的java HashMap既不属于null也不是MutableSetWrapper,所以讲被传给JSetWrapper包装起来,进去看看吧

 case class JSetWrapper[A](underlying: ju.Set[A]) extends mutable.AbstractSet[A] with mutable.Set[A] with mutable.SetLike[A, JSetWrapper[A]] {

    override def size = underlying.size

    def iterator = underlying.iterator

    def contains(elem: A): Boolean = underlying.contains(elem)

    def +=(elem: A): this.type = { underlying add elem; this }
def -=(elem: A): this.type = { underlying remove elem; this } override def add(elem: A): Boolean = underlying add elem
override def remove(elem: A): Boolean = underlying remove elem
override def clear() = underlying.clear() override def empty = JSetWrapper(new ju.HashSet[A])
// Note: Clone cannot just call underlying.clone because in Java, only specific collections
// expose clone methods. Generically, they're protected.
override def clone() =
new JSetWrapper[A](new ju.LinkedHashSet[A](underlying))
}

代码不算长,注意,它继承了scala.collection.mutable.Set,因此我们可以说这个包装类是一个货真价实的scala Set,它只是把常用的Set接口复写了一遍,实现调用的则是底层java类的api。现在终于明白了吧,所谓“转换”只是将java类的外面套了一层scala的接口而已~

scala成长之路(3)隐式转换的更多相关文章

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

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

  2. Scala 系列(十三)—— 隐式转换和隐式参数

    一.隐式转换 1.1 使用隐式转换 隐式转换指的是以implicit关键字声明带有单个参数的转换函数,它将值从一种类型转换为另一种类型,以便使用之前类型所没有的功能.示例如下: // 普通人 clas ...

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

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

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

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

  5. Scala之隐式转换implicit详解

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

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

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

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

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

  8. Scala学习之路 (八)Scala的隐式转换和隐式参数

    一.概念 Scala 2.10引入了一种叫做隐式类的新特性.隐式类指的是用implicit关键字修饰的类.在对应的作用域内,带有这个关键字的类的主构造函数可用于隐式转换. 隐式转换和隐式参数是Scal ...

  9. Scala 学习之路(十三)—— 隐式转换和隐式参数

    一.隐式转换 1.1 使用隐式转换 隐式转换指的是以implicit关键字声明带有单个参数的转换函数,它将值从一种类型转换为另一种类型,以便使用之前类型所没有的功能.示例如下: // 普通人 clas ...

随机推荐

  1. 一個新的面試題目,leetcode上面可以找到shortest palindrome

    記錄一下新的面試題目,其實題目是舊的,只是我才見到.以前研究過,只不過以前的解法不容易理解,現在有了新的遞歸解法.記錄一下. public String shortestPalindrome(Stri ...

  2. 点触科技安全验证新模式与逐浪CMS3.9.3新功能预览

    是颠覆传统字符验证码的新一代验证码系统.用户通过点击.拖动等有趣方式即可完成验证,防止机器攻击, 应用在注册.登录.数据访问保护.黄牛刷单等场景. 只要在逐浪CMS后台-系统-配置 ,进行简单的配置, ...

  3. mysql 5.7版本如何修改密码

    这是官方截图,mysql5.7安装后,会有一个默认密码,保存在mysql.log里面,找的他,并更改 官方文档地址 https://dev.mysql.com/doc/refman/5.7/en/li ...

  4. JSP简要

    本篇知识导图 说起JSP,当年做课程设计什么的都用的这个,虽然技术比较古老,但是还是挺广泛使用的. JSP是一种前台的展现语言,在mvc里面属于表现层.它主要由静态,动态两部分组成,静态包括HTML, ...

  5. 一个程序猿试用有道云笔记VIP功能体验

    熟悉我的朋友应该知道,我有一个微信公众号,叫做"汪子熙", 我会定期在上面推送技术文章. 而我绝大多数技术文章都是在每天上下班的地铁上用手机写的,然后到家后同步到电脑上,进行发表. ...

  6. ABAP git客户端

    Jerry习惯把自己写的小程序放到自己的github上:https://github.com/i042416 对于写的ABAP程序,需要先把SAPGUI里的代码手动拷贝到本地,然后用git客户端pus ...

  7. Android 高级UI设计笔记09:Android实现无限滚动列表

    1. 无限滚动列表应用场景: ListView和GridView已经成为原生的Android应用实现中两个最流行的设计模式.目前,这些模式被大量的开发者使用,主要是因为他们是简单而直接的实现,同时他们 ...

  8. React中最基础的jsx语法

    import React, { Component } from 'react'; class App extends Component { render() { return ( <div ...

  9. javaw.exe 和java.exe的区别

    1.java启动的程序是命令行程序或阻塞程序,如果该程序未执行完毕或未被关闭,则所打开的命令行将被阻塞,不能执行其它命令如dir等,可以通过Ctrl+C等方式关闭程序:2.javaw启动的程序是窗口程 ...

  10. html5中event获取data和class

    获取data和class var tare=$(e.relatedTarget).data("id");var tar=event.target;console.log(tare) ...