From 3bfd137f3228b08cf60e941cf3577f6959a99755 Mon Sep 17 00:00:00 2001 From: luoxiang <2806718453@qq.com> Date: Sun, 12 May 2019 23:14:33 +0800 Subject: [PATCH] =?UTF-8?q?=E9=9A=90=E5=BC=8F=E8=BD=AC=E6=8D=A2=E5=92=8C?= =?UTF-8?q?=E9=9A=90=E5=BC=8F=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- notes/Scala类和对象.md | 4 +- notes/Scala类型参数.md | 154 ++++++++++ notes/Scala类型参数化.md | 0 notes/Scala隐式转换和隐式参数.md | 358 ++++++++++++++++++++++ pictures/scala-richInt.png | Bin 0 -> 11413 bytes pictures/scala-视图界定.png | Bin 0 -> 20805 bytes 6 files changed, 514 insertions(+), 2 deletions(-) create mode 100644 notes/Scala类型参数.md delete mode 100644 notes/Scala类型参数化.md create mode 100644 notes/Scala隐式转换和隐式参数.md create mode 100644 pictures/scala-richInt.png create mode 100644 pictures/scala-视图界定.png diff --git a/notes/Scala类和对象.md b/notes/Scala类和对象.md index fd8b7f4..df80bd5 100644 --- a/notes/Scala类和对象.md +++ b/notes/Scala类和对象.md @@ -178,7 +178,7 @@ object Person { + 写在主构造器中的代码块会在类初始化的时候被执行,功能类似于Java的静态代码块`static{}` ```scala -class Person(name: String, age: Int) { +class Person(val name: String, val age: Int) { println("功能类似于Java的静态代码块static{}") @@ -210,7 +210,7 @@ heibaiying:20 + 每个辅助构造器必须以主构造器或其他的辅助构造器的调用开始。 ```scala -class Person(name: String, age: Int) { +class Person(val name: String, val age: Int) { private var birthday = "" diff --git a/notes/Scala类型参数.md b/notes/Scala类型参数.md new file mode 100644 index 0000000..173cdb8 --- /dev/null +++ b/notes/Scala类型参数.md @@ -0,0 +1,154 @@ +# 类型参数 + +## 一、泛型 + +Scala支持类型参数化,使得我们能够编写泛型程序。 + +### 1.1 泛型类 + +Java中使用`<>`符号来定义类型参数,Scala中使用`[]`来定义类型参数。 + +```scala +class Pair[T, S](val first: T, val second: S) { + override def toString: String = first + ":" + second +} +``` + +```scala +object ScalaApp extends App { + + // 使用时候你直接指定参数类型,也可以不指定,由程序自动推断 + val pair01 = new Pair("heibai01", 22) + val pair02 = new Pair[String,Int]("heibai02", 33) + + println(pair01) + println(pair02) +} +``` + +### 1.2 泛型方法 + +函数和方法也支持类型参数。 + +```scala +object Utils { + def getHalf[T](a: Array[T]): Int = a.length / 2 +} +``` + +## 二、类型限定 + +### 2.1 类型上界 + +对于对象之间进行大小比较,Scala和Java一样,都要求比较的对象需要实现`java.lang.Comparable`接口。 + +所以如果想对泛型进行比较,需要限定类型上界,语法为` S <: T`,代表S必须是类型T的子类或其本身。示例如下: + +```scala +// 使用 <: 符号,限定T必须是Comparable[T]的子类型 +class Pair[T <: Comparable[T]](val first: T, val second: T) { + // 返回较小的值 + def smaller: T = if (first.compareTo(second) < 0) first else second +} +``` + +```scala +// 测试代码 +val pair = new Pair("abc", "abcd") +println(pair.smaller) // 输出 abc +``` + +>注:如果你想要在Java中实现类型变量限定,需要使用关键字extends来实现,对于的Java代码如下: +> +>```java +>public class Pair> { +> private T first; +> private T second; +> Pair(T first, T second) { +> this.first = first; +> this.second = second; +> } +> public T smaller() { +> return first.compareTo(second) < 0 ? first : second; +> } +>} +>``` + +### 2.2 视图界定 & 类型约束 + +#### 1.视图界定 + +在上面的例子中,如果你使用Int类型或者Double等类型进行测试,点击运行后,你会发现程序根本无法通过编译: + +```scala +val pair1 = new Pair(10, 12) +val pair2 = new Pair(10.0, 12.0) +``` + +之所以出现这样的问题,是因为在Scala中Int并没有实现Comparable,真正实现Comparable接口的是RichInt。在日常的编程中之所以你能够执行`3>2`这样的判断操作,是因为程序执行了隐式转换,将Int转换为RichInt。 + +![scala-richInt](D:\BigData-Notes\pictures\scala-richInt.png) + +直接继承Java中Comparable接口的是特质Ordered,RichInt混入了该特质,Ordered源码如下: + +```scala +// 除了compareTo方法外,还提供了额外的关系符方法 +trait Ordered[A] extends Any with java.lang.Comparable[A] { + def compare(that: A): Int + def < (that: A): Boolean = (this compare that) < 0 + def > (that: A): Boolean = (this compare that) > 0 + def <= (that: A): Boolean = (this compare that) <= 0 + def >= (that: A): Boolean = (this compare that) >= 0 + def compareTo(that: A): Int = compare(that) +} +``` + +所以要想在泛型中解决这个问题,需要使用视图界定: + +```scala +// 视图界定符号 <% +class Pair[T <% Comparable[T]](val first: T, val second: T) { + // 返回较小的值 + def smaller: T = if (first.compareTo(second) < 0) first else second +} +``` + +> 注:由于直接继承Java中Comparable接口的是特质Ordered,所以也可以使用如下的视图界定: +> +> ```scala +> class Pair[T <% Ordered[T]](val first: T, val second: T) { +> +> def smaller: T = if (first.compareTo(second) < 0) first else second +> +> } +> ``` + +#### 2. 类型约束 + +如果你用的Scala是2.11+,则视图界定已经不推荐使用,官方推荐使用类型约束(type constraint)来实现同样的功能: + +```scala +class Pair[T](val first: T, val second: T)(implicit ev: T => Comparable[T]) { + + def smaller: T = if (first.compareTo(second) < 0) first else second + +} +``` + +### 2.3 上下文界定 + +上下文界定的形式为`T:M`,它要求必须存在一个类型为M[T]的隐式值,当你声明一个使用隐式值的方法时,需要添加一个隐式参数。上面的程序也可以使用上下文界定进行如下改写: + +```scala +class Pair[T](val first: T, val second: T) { + + def smaller(implicit ord: Ordering[T]): T = if (ord.compare(first, second) < 0) first else second + +} +``` + +### 2.4 类型下界 + + + +## 三、类型通配符 \ No newline at end of file diff --git a/notes/Scala类型参数化.md b/notes/Scala类型参数化.md deleted file mode 100644 index e69de29..0000000 diff --git a/notes/Scala隐式转换和隐式参数.md b/notes/Scala隐式转换和隐式参数.md new file mode 100644 index 0000000..c95e63e --- /dev/null +++ b/notes/Scala隐式转换和隐式参数.md @@ -0,0 +1,358 @@ +# 隐式转换和隐式参数 + + + + +## 一、隐式转换 + +### 1.1 使用隐式转换 + +隐式转换指的是以`implicit`关键字声明带有单个参数的转换函数,它将值从一种类型转换为另一种类型,以便使用之前类型所没有的功能。示例如下: + +```scala +// 普通人 +class Person(val name: String) + +// 雷神 +class Thor(val name: String) { + // 正常情况下只有雷神才能举起雷神之锤 + def hammer(): Unit = { + println(name + "举起雷神之锤") + } +} + +object Thor extends App { + // 定义隐式转换方法 将普通人转换为雷神 通常建议方法名使用source2Target,即:被转换对象To转换对象 + implicit def person2Thor(p: Person): Thor = new Thor(p.name) + // 这样普通人也能举起雷神之锤 + new Person("普通人").hammer() +} + +输出: 普通人举起雷神之锤 +``` + +
+ +### 1.2 隐式转换规则 + +并不是你使用`implicit`转换后,隐式转换就一定会发生,比如上面如果不调用`hammer()`方法的时候,普通人就还是普通人。通常程序会在以下情况下尝试执行隐式转换: + ++ 当对象访问一个不存在的成员时,即调用的方法不存在或者访问的成员变量不存在; ++ 当对象调用某个方法,该方法存在,但是方法的声明参数与传入参数不匹配时。 + +而在以下三种情况下编译器不会尝试执行隐式转换: + ++ 如果代码能够在不使用隐式转换的前提下通过编译,则不会使用隐式转换; ++ 编译器不会尝试同时执行多个转换,比如`convert1(convert2(a))*b`; ++ 转换存在二义性,也不会发生转换。 + +这里首先解释一下二义性,上面的代码进行如下修改,由于两个隐式转换都是生效的,所以就存在了二义性: + +```scala +//两个隐式转换都是有效的 +implicit def person2Thor(p: Person): Thor = new Thor(p.name) +implicit def person2Thor2(p: Person): Thor = new Thor(p.name) +// 此时下面这段语句无法通过编译 +new Person("普通人").hammer() +``` + +其次再解释一下多个转换的问题: + +```scala +class ClassA { + override def toString = "This is Class A" +} + +class ClassB { + override def toString = "This is Class B" + def printB(b: ClassB): Unit = println(b) +} + +class ClassC + +class ClassD + +object ImplicitTest extends App { + implicit def A2B(a: ClassA): ClassB = { + println("A2B") + new ClassB + } + + implicit def C2B(c: ClassC): ClassB = { + println("C2B") + new ClassB + } + + implicit def D2C(d: ClassD): ClassC = { + println("D2C") + new ClassC + } + + // 这行代码无法通过编译,因为要调用到printB方法,需要执行两次转换C2B(D2C(ClassD)) + new ClassD().printB(new ClassA) + + /* + * 下面的这一行代码虽然也进行了两次隐式转换,但是两次的转换对象并不是一个对象,所以它是生效的: + * 转换流程如下: + * 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,所以虽然是一行代码两次转换,但是仍然是有效转换 + */ + new ClassC().printB(new ClassA) +} + +// 输出: +C2B +A2B +This is Class B +``` + +
+ +### 1.3 引入隐式转换 + +隐式转换的可以定义在以下三个地方: + ++ 定义在原类型的伴生对象中; ++ 直接定义在执行代码的上下文作用域中; ++ 统一定义在一个文件中,在使用时候导入。 + +上面我们使用的方法相当于直接定义在执行代码的作用域中,下面分别给出其他两种定义的代码示例: + +定义在原类型的伴生对象中: + +```scala +class Person(val name: String) +// 在伴生对象中定义隐式转换函数 +object Person{ + implicit def person2Thor(p: Person): Thor = new Thor(p.name) +} +``` + +```scala +class Thor(val name: String) { + def hammer(): Unit = { + println(name + "举起雷神之锤") + } +} +``` + +```scala +// 使用示例 +object ScalaApp extends App { + new Person("普通人").hammer() +} +``` + +定义在一个公共的对象中: + +```scala +object Convert { + implicit def person2Thor(p: Person): Thor = new Thor(p.name) +} +``` + +```scala +// 导入Convert下所有的隐式转换函数 +import com.heibaiying.Convert._ + +object ScalaApp extends App { + // 这样普通人也能举起雷神之锤 + new Person("普通人").hammer() +} +``` + +> 注:Scala中隐式转换函数大部分定义在`Predef.scala`中,你可以打开源文件查看,也可以在Scala交互式命令行中采用`:implicit -v`查看全部隐式转换函数。 + +
+ +## 二、隐式参数 + +### 2.1 使用隐式参数 + +函数或方法可以带有一个标记为`implicit`的参数列表,这种情况下,编译器将会查找默认值,提供给函数调用。 + +```scala +// 定义分隔符类 +class Delimiters(val left: String, val right: String) + +object ScalaApp extends App { + + // 进行格式化输出 + def formatted(context: String)(implicit deli: Delimiters): Unit = { + println(deli.left + context + deli.right) + } + + // 定义一个隐式默认值 使用左右中括号作为分隔符 + implicit val bracket = new Delimiters("(", ")") + formatted("this is context") // 输出: (this is context) +} +``` + +关于隐式参数,有两点需要注意: + +1.我们上面定义`formatted`函数的时候使用了柯里化,如果你不使用柯里化表达式,按照通常习惯只有下面两种写法: + +```scala +//这种写法没有语法错误,但是无法通过编译 +def formatted(implicit context: String, deli: Delimiters): Unit = { + println(deli.left + context + deli.right) +} +// 不存在这种写法,IDEA直接会直接提示语法错误 +def formatted( context: String, implicit deli: Delimiters): Unit = { + println(deli.left + context + deli.right) +} +``` + +上面第一种写法编译的时候会出现下面所示`error`信息,从中也可以看出`implicit`是作用于参数列表中每个参数的,这显然不是我们想要到达的效果,所以上面的写法采用了柯里化。 + +``` +not enough arguments for method formatted: (implicit context: String, implicit deli: com.heibaiying.Delimiters) +``` + +2.第二个问题和隐式函数一样,隐式默认值不能存在二义性,否则无法通过编译,示例如下: + +```scala +implicit val bracket = new Delimiters("(", ")") +implicit val brace = new Delimiters("{", "}") +formatted("this is context") +``` + +上面代码无法通过编译,出现错误提示`ambiguous implicit values`,即隐式值存在冲突。 + +
+ +### 2.2 引入隐式参数 + +引入隐式参数和引入隐式转换函数方法是一样的,有以下三种方式: + +- 定义在隐式参数对应类的伴生对象中; +- 直接定义在执行代码的上下文作用域中; +- 统一定义在一个文件中,在使用时候导入。 + +我们上面示例程序相当于直接定义执行代码的上下文作用域中,下面给出其他两种方式的示例: + +定义在隐式参数对应类的伴生对象中; + +```scala +class Delimiters(val left: String, val right: String) + +object Delimiters { + implicit val bracket = new Delimiters("(", ")") +} +``` + +```scala +// 此时执行代码的上下文中不用定义 +object ScalaApp extends App { + + def formatted(context: String)(implicit deli: Delimiters): Unit = { + println(deli.left + context + deli.right) + } + formatted("this is context") +} +``` + +统一定义在一个文件中,在使用时候导入: + +```scala +object Convert { + implicit val bracket = new Delimiters("(", ")") +} +``` + +```scala +// 在使用的时候导入 +import com.heibaiying.Convert.bracket + +object ScalaApp extends App { + def formatted(context: String)(implicit deli: Delimiters): Unit = { + println(deli.left + context + deli.right) + } + formatted("this is context") // 输出: (this is context) +} +``` + +
+ +### 2.3 利用隐式参数进行隐式转换 + +```scala +def smaller[T] (a: T, b: T) = if (a < b) a else b +``` + +在Scala中如果定义了一个如上所示的比较对象大小的泛型方法,你会发现无法通过编译。对于对象之间进行大小比较,Scala和Java一样,都要求被比较的对象需要实现java.lang.Comparable接口。 + +在Scala中,直接继承Java中Comparable接口的是特质Ordered,它在继承compareTo方法的基础上,额外定义了关系符方法,源码如下: + +```scala +trait Ordered[A] extends Any with java.lang.Comparable[A] { + def compare(that: A): Int + def < (that: A): Boolean = (this compare that) < 0 + def > (that: A): Boolean = (this compare that) > 0 + def <= (that: A): Boolean = (this compare that) <= 0 + def >= (that: A): Boolean = (this compare that) >= 0 + def compareTo(that: A): Int = compare(that) +} +``` + +所以要想在泛型中解决这个问题,有两种方法: + +#### 1. 使用视图界定 + +```scala +object Pair extends App { + + // 视图界定 + def smaller[T<% Ordered[T]](a: T, b: T) = if (a < b) a else b + + println(smaller(1,2)) //输出 1 +} +``` + +视图限定限制了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`。 + +
+ + + +#### 2. 利用隐式参数进行隐式转换 + +在Scala2.11+后,视图界定被标识为废弃,官方推荐使用类型限定来解决上面的问题,本质上就是使用隐式参数进行隐式转换。 + +```scala +object Pair extends App { + + // order既是一个隐式参数也是一个隐式转换,即如果a不存在 < 方法,则转换为order(a) Ordered[T]) = if (a < b) a else b + + println(smaller(1,2)) //输出 1 +} +``` + + + +## 参考资料 + +1. Martin Odersky . Scala编程(第3版)[M] . 电子工业出版社 . 2018-1-1 +2. 凯.S.霍斯特曼 . 快学Scala(第2版)[M] . 电子工业出版社 . 2017-7 + + + diff --git a/pictures/scala-richInt.png b/pictures/scala-richInt.png new file mode 100644 index 0000000000000000000000000000000000000000..b38b421ff7959c434889db09838c40fb7a8323b1 GIT binary patch literal 11413 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 literal 0 HcmV?d00001 diff --git a/pictures/scala-视图界定.png b/pictures/scala-视图界定.png new file mode 100644 index 0000000000000000000000000000000000000000..77923f5d398eb0cda08af635671217007fbe12e3 GIT binary patch literal 20805 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

##ej_49iTIvGq`*QR@RrqgR7o|zyTNn7~#FG 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 Xk1a-7YqyhhOR)Pv!h!1jCsY3h+MWyP literal 0 HcmV?d00001