优化阅读格式

This commit is contained in:
heibaiying
2019-07-31 17:18:07 +08:00
parent ceb868fe13
commit ca7c99802b
91 changed files with 4059 additions and 4058 deletions

View File

@ -16,7 +16,7 @@
### 1.1 使用隐式转换
隐式转换指的是以`implicit`关键字声明带有单个参数的转换函数,它将值从一种类型转换为另一种类型,以便使用之前类型所没有的功能。示例如下:
隐式转换指的是以 `implicit` 关键字声明带有单个参数的转换函数,它将值从一种类型转换为另一种类型,以便使用之前类型所没有的功能。示例如下:
```scala
// 普通人
@ -31,7 +31,7 @@ class Thor(val name: String) {
}
object Thor extends App {
// 定义隐式转换方法 将普通人转换为雷神 通常建议方法名使用source2Target,即被转换对象To转换对象
// 定义隐式转换方法 将普通人转换为雷神 通常建议方法名使用 source2Target,即:被转换对象 To 转换对象
implicit def person2Thor(p: Person): Thor = new Thor(p.name)
// 这样普通人也能举起雷神之锤
new Person("普通人").hammer()
@ -44,7 +44,7 @@ object Thor extends App {
### 1.2 隐式转换规则
并不是你使用`implicit`转换后,隐式转换就一定会发生,比如上面如果不调用`hammer()`方法的时候,普通人就还是普通人。通常程序会在以下情况下尝试执行隐式转换:
并不是你使用 `implicit` 转换后,隐式转换就一定会发生,比如上面如果不调用 `hammer()` 方法的时候,普通人就还是普通人。通常程序会在以下情况下尝试执行隐式转换:
+ 当对象访问一个不存在的成员时,即调用的方法不存在或者访问的成员变量不存在;
+ 当对象调用某个方法,该方法存在,但是方法的声明参数与传入参数不匹配时。
@ -52,7 +52,7 @@ object Thor extends App {
而在以下三种情况下编译器不会尝试执行隐式转换:
+ 如果代码能够在不使用隐式转换的前提下通过编译,则不会使用隐式转换;
+ 编译器不会尝试同时执行多个转换,比如`convert1(convert2(a))*b`
+ 编译器不会尝试同时执行多个转换,比如 `convert1(convert2(a))*b`
+ 转换存在二义性,也不会发生转换。
这里首先解释一下二义性,上面的代码进行如下修改,由于两个隐式转换都是生效的,所以就存在了二义性:
@ -97,16 +97,16 @@ object ImplicitTest extends App {
new ClassC
}
// 这行代码无法通过编译因为要调用到printB方法需要执行两次转换C2B(D2C(ClassD))
// 这行代码无法通过编译,因为要调用到 printB 方法,需要执行两次转换 C2B(D2C(ClassD))
new ClassD().printB(new ClassA)
/*
* 下面的这一行代码虽然也进行了两次隐式转换,但是两次的转换对象并不是一个对象,所以它是生效的:
* 转换流程如下:
* 1. ClassC中并没有printB方法,因此隐式转换为ClassB,然后调用printB方法;
* 2. 但是printB参数类型为ClassB,然而传入的参数类型是ClassA,所以需要将参数ClassA转换为ClassB,这是第二次;
* 1. ClassC 中并没有 printB 方法,因此隐式转换为 ClassB,然后调用 printB 方法;
* 2. 但是 printB 参数类型为 ClassB,然而传入的参数类型是 ClassA,所以需要将参数 ClassA 转换为 ClassB,这是第二次;
* 即: C2B(ClassC) -> ClassB.printB(ClassA) -> ClassB.printB(A2B(ClassA)) -> ClassB.printB(ClassB)
* 转换过程1的对象是ClassC,而转换过程2的转换对象是ClassA,所以虽然是一行代码两次转换,但是仍然是有效转换
* 转换过程 1 的对象是 ClassC,而转换过程 2 的转换对象是 ClassA,所以虽然是一行代码两次转换,但是仍然是有效转换
*/
new ClassC().printB(new ClassA)
}
@ -163,7 +163,7 @@ object Convert {
```
```scala
// 导入Convert下所有的隐式转换函数
// 导入 Convert 下所有的隐式转换函数
import com.heibaiying.Convert._
object ScalaApp extends App {
@ -171,7 +171,7 @@ object ScalaApp extends App {
}
```
> Scala自身的隐式转换函数大部分定义在`Predef.scala`中你可以打开源文件查看也可以在Scala交互式命令行中采用`:implicit -v`查看全部隐式转换函数。
> Scala 自身的隐式转换函数大部分定义在 `Predef.scala` 中,你可以打开源文件查看,也可以在 Scala 交互式命令行中采用 `:implicit -v` 查看全部隐式转换函数。
<br/>
@ -179,7 +179,7 @@ object ScalaApp extends App {
### 2.1 使用隐式参数
在定义函数或方法时可以使用标记为`implicit`的参数,这种情况下,编译器将会查找默认值,提供给函数调用。
在定义函数或方法时可以使用标记为 `implicit` 的参数,这种情况下,编译器将会查找默认值,提供给函数调用。
```scala
// 定义分隔符类
@ -200,20 +200,20 @@ object ScalaApp extends App {
关于隐式参数,有两点需要注意:
1.我们上面定义`formatted`函数的时候使用了柯里化,如果你不使用柯里化表达式,按照通常习惯只有下面两种写法:
1.我们上面定义 `formatted` 函数的时候使用了柯里化,如果你不使用柯里化表达式,按照通常习惯只有下面两种写法:
```scala
// 这种写法没有语法错误,但是无法通过编译
def formatted(implicit context: String, deli: Delimiters): Unit = {
println(deli.left + context + deli.right)
}
// 不存在这种写法IDEA直接会直接提示语法错误
// 不存在这种写法IDEA 直接会直接提示语法错误
def formatted( context: String, implicit deli: Delimiters): Unit = {
println(deli.left + context + deli.right)
}
```
上面第一种写法编译的时候会出现下面所示`error`信息,从中也可以看出`implicit`是作用于参数列表中每个参数的,这显然不是我们想要到达的效果,所以上面的写法采用了柯里化。
上面第一种写法编译的时候会出现下面所示 `error` 信息,从中也可以看出 `implicit` 是作用于参数列表中每个参数的,这显然不是我们想要到达的效果,所以上面的写法采用了柯里化。
```
not enough arguments for method formatted:
@ -228,7 +228,7 @@ implicit val brace = new Delimiters("{", "}")
formatted("this is context")
```
上面代码无法通过编译,出现错误提示`ambiguous implicit values`,即隐式值存在冲突。
上面代码无法通过编译,出现错误提示 `ambiguous implicit values`,即隐式值存在冲突。
@ -291,7 +291,7 @@ object ScalaApp extends App {
def smaller[T] (a: T, b: T) = if (a < b) a else b
```
在Scala中如果定义了一个如上所示的比较对象大小的泛型方法你会发现无法通过编译。对于对象之间进行大小比较ScalaJava一样都要求被比较的对象需要实现java.lang.Comparable接口。在Scala中直接继承JavaComparable接口的是特质Ordered它在继承compareTo方法的基础上额外定义了关系符方法源码如下:
Scala 中如果定义了一个如上所示的比较对象大小的泛型方法你会发现无法通过编译。对于对象之间进行大小比较ScalaJava 一样,都要求被比较的对象需要实现 java.lang.Comparable 接口。在 Scala 中,直接继承 JavaComparable 接口的是特质 Ordered它在继承 compareTo 方法的基础上,额外定义了关系符方法,源码如下:
```scala
trait Ordered[A] extends Any with java.lang.Comparable[A] {
@ -318,14 +318,14 @@ object Pair extends App {
}
```
视图限定限制了T可以通过隐式转换`Ordered[T]`,即对象一定可以进行大小比较。在上面的代码中`smaller(1,2)`中参数`1``2`实际上是通过定义在`Predef`中的隐式转换方法`intWrapper`转换为`RichInt`
视图限定限制了 T 可以通过隐式转换 `Ordered[T]`,即对象一定可以进行大小比较。在上面的代码中 `smaller(1,2)` 中参数 `1``2` 实际上是通过定义在 `Predef` 中的隐式转换方法 `intWrapper` 转换为 `RichInt`
```scala
// Predef.scala
@inline implicit def intWrapper(x: Int) = new runtime.RichInt(x)
```
为什么要这么麻烦执行隐式转换原因是Scala中的Int类型并不能直接进行比较因为其没有实现`Ordered`特质,真正实现`Ordered`特质的是`RichInt`
为什么要这么麻烦执行隐式转换,原因是 Scala 中的 Int 类型并不能直接进行比较,因为其没有实现 `Ordered` 特质,真正实现 `Ordered` 特质的是 `RichInt`
<div align="center"> <img src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/scala-richInt.png"/> </div>
@ -333,12 +333,12 @@ object Pair extends App {
#### 2. 利用隐式参数进行隐式转换
Scala2.11+后,视图界定被标识为废弃,官方推荐使用类型限定来解决上面的问题,本质上就是使用隐式参数进行隐式转换。
Scala2.11+ 后,视图界定被标识为废弃,官方推荐使用类型限定来解决上面的问题,本质上就是使用隐式参数进行隐式转换。
```scala
object Pair extends App {
// order既是一个隐式参数也是一个隐式转换即如果a不存在 < 方法则转换为order(a)<b
// order 既是一个隐式参数也是一个隐式转换,即如果 a 不存在 < 方法,则转换为 order(a)<b
def smaller[T](a: T, b: T)(implicit order: T => Ordered[T]) = if (a < b) a else b
println(smaller(1,2)) //输出 1
@ -349,8 +349,8 @@ object Pair extends App {
## 参考资料
1. Martin Odersky . Scala编程(第3版)[M] . 电子工业出版社 . 2018-1-1
2. 凯.S.霍斯特曼 . 快学Scala(第2版)[M] . 电子工业出版社 . 2017-7
1. Martin Odersky . Scala 编程 (第 3 版)[M] . 电子工业出版社 . 2018-1-1
2. 凯.S.霍斯特曼 . 快学 Scala(第 2 版)[M] . 电子工业出版社 . 2017-7