文档章节

Scala Implicit Conversion

秋风醉了
 秋风醉了
发布于 2015/04/28 17:14
字数 1351
阅读 74
收藏 0
点赞 0
评论 0

Scala Implicit Conversion

从一个简单例子出发,我们定义一个函数接受一个字符串参数,并进行输出

def func(msg: String): Unit = println(msg)

这个函数在func("11")调用时候正常,但是在执行func(11)或func(1.1)时候就会报error: type mismatch的错误。这个问题很好解决:

  1. 针对特定的参数类型,重载多个func函数,这个不难,传统JAVA中的思路,但是需要定义多个函数

  2. 使用超类型,比如使用AnyVal,Any,这样的话比较麻烦,需要在函数中针对特定的逻辑做类型转化,从而进一步处理

上面两个方法使用的是传统JAVA思路,虽然都可以解决该问题,但是缺点是不够简洁;在充满了语法糖的Scala中,针对类型转换提供了特有的implicit隐式转换的功能。

隐式转化是一个函数,可以针对一个变量在需要的时候自动的进行类型转换;针对上面的例子,我们可以定义intToString函数

def func(msg: String): Unit = println(msg)

implicit def intToString(i: Int): String = i.toString

func("hello world!")
func(123)

运行并输出,

C:\WorkSpace6-scala\scala-train\src\com\usoft>scala implicit.scala

warning: there was one feature warning; re-run with -feature for details

one warning found

hello world!

123

此时在调用func(11)时候, scala会自动针对11进行intToString函数的调用, 从而实现可以在func函数已有的类型上提供了新的类型支持,这里有几点要说一下:

  1. 隐式转换的核心是from类型和to类型,至于函数名称并不重要;上面我们取为intToString,只是为了直观,int2str的功能是一样的;隐式转换函数只关心from-to类型之间的匹配,比如我们需要to类型,但是提供了from类型,那么相应的implicit函数就会调用 

  2. 隐式转换只关心类型,所以如果同时定义两个隐式转换函数,from/to类型相同,但是函数名称不同,这个时候函数调用过程中如果需要进行类型转换,就会报ambiguous二义性的错误,即不知道使用哪个隐式转换函数进行转换

上面我们看到的例子是将函数的参数从一个类型自动转换为一个类型的例子,在Scala中,除了针对函数参数类型进行转换以外,还可以对函数的调用者的类型进行转换。

比如A+B,上面我们谈到是针对B进行类型自动转换, 其实可以在A上做类型转换,下面我们拿一个例子来说明

class IntWritable(_value: Int) {
  var value = _value

  def +(that: IntWritable): IntWritable = {
    new IntWritable(that.value + value)
  }
}

implicit def intToWritable(int: Int) = new IntWritable(int)
new IntWritable(10) + 10

上面我们首先定义了一个类:IntWritable,并为int提供了一个隐式类型转换intToWritable,从而可以使得IntWritable的+函数在原先只接受IntWritable类型参数的基础上,接受一个Int类型的变量进行运算,即new IntWritable(10) + 10可以正常运行

现在换一个角度将"new IntWritable(10) + 10" 换为"10 + new IntWritable(10)"会是什么结果呢?会报错误吗?

如下代码,

class IntWritable(_value: Int) {
  var value = _value

  def +(that: IntWritable): IntWritable = {
    new IntWritable(that.value + value)
  }
}

implicit def intToWritable(int: Int) = new IntWritable(int)
//implicit def writableToInt(that: IntWritable) = that.value


//val result1 = new IntWritable(10) + 10
val result2 = 10 + new IntWritable(10)

//println(result1.getClass.getName)
println(result2.getClass.getName) //Main$$anon$1$IntWritable

按道理是应该报错误,首先一个Int内置类型的+函数,没有IntWritable这个参数类型;其次,我们没有针对 IntWritable 类型提供到 Int 的隐式转换,

即没有提供writableToInt的implicit函数。

但是结果是什么?

10 + new IntWritable(10) 是可以正常运行的,而且整个表达的类型为IntWritable,而不是Int,即Int的10被intToWritable函数隐式函数转换为IntWritable类型;

结论:隐式转换可以针对函数参数类型和函数对象进行类型转换;现在问题来了,看下面的例子

class IntWritable(_value: Int) {
  var value = _value

  def +(that: IntWritable): IntWritable = {
    new IntWritable(that.value + value)
  }
}

implicit def intToWritable(int: Int) = new IntWritable(int)
implicit def writableToInt(that: IntWritable) = that.value


val result1 = new IntWritable(10) + 10
val result2 = 10 + new IntWritable(10)

println(result1.getClass.getName) //Main$$anon$1$IntWritable
println(result2.getClass.getName) //int

在上面的IntWritable类的基础上,我们提供了两个隐式类型转换函数,即Int和IntWritable之间的双向转换;这样的情况下result1和result2两个变量的类型是什么?

答案:result1的类型为IntWritable, result2的类型Int;很好理解, result1中的Int类型的10被intToWritable隐式转换为IntWritable;而result2中的IntWritable(10)被writableToInt 隐式转换为Int类型;

你肯定会问?result2中为什么不是像上面的例子一样, 把Int类型的10隐式转换为IntWritable类型呢?原因就是隐式转换的优先级;

发生类型不匹配的函数调用时, scala会尝试进行类型隐式转换;首先优先进行函数参数的类型转换,如果可以转换, 那么就完成函数的执行; 否则尝试去对函数调用对象的类型进行转换; 如果两个尝试都失败了,就会报方法不存在或者类型不匹配的错误;

OK, Scala的隐式转换是Scala里面随处可见的语法。

================END================

本文转载自:https://github.com/ColZer/DigAndBuried/blob/master/spark/scala-implicit.md

共有 人打赏支持
秋风醉了
粉丝 223
博文 581
码字总数 411013
作品 0
东城
程序员
Scala学习笔记(4):关于String

Scala并没有定义自己的String类型,而是直接借用了Java中的String,所以如果你定义一个String类型的值: val str = "hello" 返回的值的类型直接就是java.lang.String。然而,我们知道在Scala...

chengyao2 ⋅ 2013/05/14 ⋅ 0

Scala implicit implicit基本含义

Scala implicit implicit基本含义 在Scala中有一个关键字是implicit, 之前一直不知道这个货是干什么的,今天整理了一下。 我们先来看一个例子: def display(input:String):Unit = println...

泳泳啊泳泳 ⋅ 01/07 ⋅ 0

Scala 特殊的对象和关键字

Option: 标准类库中的Option类型用样例类来表示那种可能存在、也可能不存在的值。 Option 有两个子类别,一个是 Some,一个是 None,当他回传 Some 的时候,代表这个函式成功地给了你一个 St...

BigMaN ⋅ 2013/09/06 ⋅ 0

Scala Implicit Parameters

Scala Implicit Parameters 如果定义函数时,标明某一参数为implicit,则这个参数是隐式参数。看起来与缺省参数(Default Parameters)类似,调用者不必在调用时指定该参数。 但是就实际运行...

秋风醉了 ⋅ 2015/04/28 ⋅ 0

Scala中隐式转换(implicit conversion)的优先顺序

在学习Scala的时候,隐式转换(implicit conversion)这个特性让我实在是闹不住啊。于是乎一边试用一边感慨:真的是太强大,太方便了。 不过,越是强大且方便的东西,越容易用出毛病来。在我...

凯奥斯 ⋅ 2013/08/06 ⋅ 0

Spark算子:RDD行动Action操作(2)–take、top、takeOrdered

take def take(num: Int): Array[T] take用于获取RDD中从0到num-1下标的元素,不排序。 scala> var rdd1 = sc.makeRDD(Seq(10, 4, 2, 12, 3)) rdd1: org.apache.spark.rdd.RDD[Int] = Paral......

chensanti234 ⋅ 2016/12/20 ⋅ 0

scala 果然是一门非常有创造力的语言

Scala作为JVM上的新语言,借鉴了OO和FP的很多思想,推荐各位有时间可以看看。FP的很多思想对于习惯了OO的人来说简直是一种冲击。 我特别喜欢Scala无处不在的Pattern Match,以及implicit的概...

xiaowenliang ⋅ 2010/12/10 ⋅ 8

Scala笔记整理(八):类型参数(泛型)与隐士转换

[TOC] 概述 类型参数是什么?类型参数其实就是Java中的泛型。大家对Java中的泛型应该有所了解,比如我们有List list = new ArrayList(),接着list.add(1),没问题,list.add("2"),然后我们l...

xpleaf ⋅ 04/23 ⋅ 0

IntelliJ Scala Plugin 1.3 EAP 1.2.67 发布

IntelliJ Scala Plugin 1.3 EAP 1.2.67 发布,此版本现已提供下载:http://download.jetbrains.com/scala/scala-intellij-bin-1.2.67.1.EAP.zip,包括大量改进: Moncole 支持 Unused impor......

oschina ⋅ 2015/01/20 ⋅ 1

Scala类型系统——高级类类型(higher-kinded types)

高级类类型就是使用其他类型构造成为一个新的类型,因此也称为 类型构造器(type constructors)。它的语法和高阶函数(higher-order functions)相似,高阶函数就是将其它函数作为参数的函数;高...

Barudisshu ⋅ 2016/06/13 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

用ZBLOG2.3博客写读书笔记网站能创造今日头条的辉煌吗?

最近两年,著名的自媒体网站今日头条可以说是火得一塌糊涂,虽然从目前来看也遇到了一点瓶颈,毕竟发展到了一定的规模,继续增长就更加难了,但如今的今日头条规模和流量已经非常大了。 我们...

原创小博客 ⋅ 今天 ⋅ 0

MyBatis四大核心概念

本文讲解 MyBatis 四大核心概念(SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession、Mapper)。 MyBatis 作为互联网数据库映射工具界的“上古神器”,训有四大“神兽”,谓之:Sql...

waylau ⋅ 今天 ⋅ 0

以太坊java开发包web3j简介

web3j(org.web3j)是Java版本的以太坊JSON RPC接口协议封装实现,如果需要将你的Java应用或安卓应用接入以太坊,或者希望用java开发一个钱包应用,那么用web3j就对了。 web3j的功能相当完整...

汇智网教程 ⋅ 今天 ⋅ 0

2个线程交替打印100以内的数字

重点提示: 线程的本质上只是一个壳子,真正的逻辑其实在“竞态条件”中。 举个例子,比如本题中的打印,那么在竞态条件中,我只需要一个方法即可; 假如我的需求是2个线程,一个+1,一个-1,...

Germmy ⋅ 今天 ⋅ 0

Springboot2 之 Spring Data Redis 实现消息队列——发布/订阅模式

一般来说,消息队列有两种场景,一种是发布者订阅者模式,一种是生产者消费者模式,这里利用redis消息“发布/订阅”来简单实现订阅者模式。 实现之前先过过 redis 发布订阅的一些基础概念和操...

Simonton ⋅ 今天 ⋅ 0

error:Could not find gradle

一.更新Android Studio后打开Project,报如下错误: Error: Could not find com.android.tools.build:gradle:2.2.1. Searched in the following locations: file:/D:/software/android/andro......

Yao--靠自己 ⋅ 昨天 ⋅ 0

Spring boot 项目打包及引入本地jar包

Spring Boot 项目打包以及引入本地Jar包 [TOC] 上篇文章提到 Maven 项目添加本地jar包的三种方式 ,本篇文章记录下在实际项目中的应用。 spring boot 打包方式 我们知道,传统应用可以将程序...

Os_yxguang ⋅ 昨天 ⋅ 0

常见数据结构(二)-树(二叉树,红黑树,B树)

本文介绍数据结构中几种常见的树:二分查找树,2-3树,红黑树,B树 写在前面 本文所有图片均截图自coursera上普林斯顿的课程《Algorithms, Part I》中的Slides 相关命题的证明可参考《算法(第...

浮躁的码农 ⋅ 昨天 ⋅ 0

android -------- 混淆打包报错 (warning - InnerClass ...)

最近做Android混淆打包遇到一些问题,Android Sdutio 3.1 版本打包的 错误如下: Android studio warning - InnerClass annotations are missing corresponding EnclosingMember annotation......

切切歆语 ⋅ 昨天 ⋅ 0

eclipse酷炫大法之设置主题、皮肤

eclipse酷炫大法 目前两款不错的eclipse 1.系统设置 Window->Preferences->General->Appearance 2.Eclipse Marketplace下载【推荐】 Help->Eclipse Marketplace->搜索‘theme’进行安装 比如......

anlve ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部