This commit is contained in:
罗祥 2019-06-04 10:35:13 +08:00
parent d09ca11daa
commit 52a294dbe6
8 changed files with 75 additions and 77 deletions

View File

@ -170,7 +170,7 @@ TODO
7. [常用集合类型之——Map & Tuple](https://github.com/heibaiying/BigData-Notes/blob/master/notes/Scala映射和元组.md)
8. [类和对象](https://github.com/heibaiying/BigData-Notes/blob/master/notes/Scala类和对象.md)
9. [继承和特质](https://github.com/heibaiying/BigData-Notes/blob/master/notes/Scala继承和特质.md)
10. [函数和闭包](https://github.com/heibaiying/BigData-Notes/blob/master/notes/Scala函数和闭包.md)
10. [函数 & 闭包 & 柯里化](https://github.com/heibaiying/BigData-Notes/blob/master/notes/Scala函数和闭包.md)
11. [模式匹配](https://github.com/heibaiying/BigData-Notes/blob/master/notes/Scala模式匹配.md)
12. [类型参数](https://github.com/heibaiying/BigData-Notes/blob/master/notes/Scala类型参数.md)
13. [隐式转换和隐式参数](https://github.com/heibaiying/BigData-Notes/blob/master/notes/Scala隐式转换和隐式参数.md)

View File

@ -20,7 +20,7 @@
### 1.1 函数与方法
Scala中函数与方法的区别非常小,如果函数作为某个对象的成员,这样的函数被称为方法,否则就是一个正常的函数。
Scala中函数与方法的区别非常小如果函数作为某个对象的成员这样的函数被称为方法否则就是一个正常的函数。
```scala
// 定义方法
@ -32,7 +32,7 @@ println(multi1(3)) //输出 9
println(multi2(3)) //输出 9
```
其实对于定义函数使用val和def并没有区别也可以使用`def`定义函数:
也可以使用`def`定义函数:
```scala
def multi3 = (x: Int) => {x * x}
@ -52,14 +52,14 @@ multi2: Int => Int = $$Lambda$1092/594363215@1dd1a777
scala> def multi3 = (x: Int) => {x * x}
multi3: Int => Int
// 如果有多个参数,则类型(参数类型,参数类型 ...=>返回值类型
// 如果有多个参数,则类型为:(参数类型,参数类型 ...=>返回值类型
scala> val multi4 = (x: Int,name: String) => {name + x * x }
multi4: (Int, String) => String = $$Lambda$1093/1039732747@2eb4fe7
```
### 1.3 一等公民&匿名函数
在Scala中函数是一等公民这意味着不仅可以定义函数并调用它们还可以用匿名的字面量来编写函数并将它们作为值进行传递:
在Scala中函数是一等公民这意味着不仅可以定义函数并调用它们还可以将它们作为值进行传递
```scala
import scala.math.ceil
@ -93,7 +93,7 @@ object ScalaApp extends App {
#### 1. 可变长度参数列表
在Java中如果你想要传递可变长度的参数列表,则需要使用`String ...args`这种语法Scala中等效的语法`args: String*`
在Java中如果你想要传递可变长度的参数,需要使用`String ...args`这种形式Scala中等效的表达`args: String*`
```scala
object ScalaApp extends App {
@ -110,7 +110,7 @@ flink
#### 2. 传递具名参数
向函数传递参数时候可以指定具体的名
向函数传递参数时候可以指定具体的参数名。
```scala
object ScalaApp extends App {
@ -127,7 +127,7 @@ object ScalaApp extends App {
#### 3. 默认值参数
可以为函数的参数指定默认值。
在定义函数时,可以为参数指定默认值。
```scala
object ScalaApp extends App {
@ -163,7 +163,7 @@ val addMore = (x: Int) => x + more
这里需要注意的是,闭包捕获的是变量本身,即是对变量本身的引用,这意味着:
+ 闭包外部对自由变量的修改,在闭包内部是可见的;
+ 闭包内部对自由变量的修改,在闭包外部也是可见的
+ 闭包内部对自由变量的修改,在闭包外部也是可见的
```scala
// 声明more变量
@ -239,7 +239,7 @@ object ScalaApp extends App {
x * x
}
// 2.定义高阶函数: 第一个参数是类型为Int => Int的函数,第二个参数为Int类型的值
// 2.定义高阶函数: 第一个参数是类型为Int => Int的函数
def multi(fun: Int => Int, x: Int) = {
fun(x) * 100
}
@ -267,8 +267,8 @@ object ScalaApp extends App {
这里当你调用curriedSum时候实际上是连着做了两次传统的函数调用实际执行的柯里化过程如下
+ 第一次调用接收一个名为x的int参数返回一个用于第二次调用的函数假设x为2则返回函数`2+y`
+ 返回的函数接收Int参数y并计算并返回值`2+3`的值。
+ 第一次调用接收一个名为`x`的Int型参数,返回一个用于第二次调用的函数,假设`x`为2则返回函数`2+y`
+ 返回的函数接收参数`y`,并计算并返回值`2+3`的值。
想要获得柯里化的中间返回的函数其实也比较简单:

View File

@ -32,8 +32,8 @@ scala> list(1) = "hive"
Scala中List具有以下两个特性
+ 同构(homogeneous)同一个List中的所有元素都必须是相同的类型
+ 协变(covariant)如果S是T的子类型那么`List[S]`就是`List[T]`的子类型,例如`List[String]``List[Object]`的子类型。
+ **同构(homogeneous)**同一个List中的所有元素都必须是相同的类型
+ **协变(covariant)**如果S是T的子类型那么`List[S]`就是`List[T]`的子类型,例如`List[String]``List[Object]`的子类型。
需要特别说明的是空列表的类型为`List[Nothing]`
@ -127,9 +127,9 @@ res2: scala.collection.immutable.Range = Range(0, 1, 2)
### 5.3 take & drop & splitAt
- take获取前n个元素
- drop删除前n个元素
- splitAt从第几个位置开始拆分。
- **take**获取前n个元素
- **drop**删除前n个元素
- **splitAt**:从第几个位置开始拆分。
```scala
scala> list take 2
@ -168,14 +168,14 @@ res7: (List[String], List[Int]) = (List(hadoop, spark, storm),List(10, 20, 30))
### 5.6 toString & mkString
toString 返回 list的字符串表现形式。
toString 返回List的字符串表现形式。
```scala
scala> list.toString
res8: String = List(hadoop, spark, storm)
```
如果想改变list的字符串表现形式可以使用mkStringmkString有三个重载方法
如果想改变List的字符串表现形式可以使用mkString。mkString有三个重载方法方法定义如下
```scala
// start前缀 sep分隔符 end:后缀
@ -335,7 +335,7 @@ res27: (List[Int], List[Int]) = (List(1, 2, 3),List(-4, 5))
### 6.3 列表检查forall & exists
forall检查List中所有元素如果所有元素都满足条件则返回true
forall检查List中所有元素如果所有元素都满足条件则返回true
```scala
scala> List(1, 2, 3, -4, 5) forall ( _ > 0 )

View File

@ -98,18 +98,18 @@ object ScalaApp extends App {
### 1.4 提取器
数组、列表和元组能使用模式匹配,都是依靠提取器(extractor)机制它们伴生对象中定义unapply或unapplySeq方法
数组、列表和元组能使用模式匹配,都是依靠提取器(extractor)机制,它们伴生对象中定义`unapply``unapplySeq`方法:
+ unapply方法用于提取固定数量的对象;
+ unapplySeq用于提取一个序列
+ **unapply**用于提取固定数量的对象;
+ **unapplySeq**用于提取一个序列;
以下是`Array.scala`类源码中定义的`unapplySeq`方法:
这里以数组为例,`Array.scala`定义了`unapplySeq`方法:
```scala
def unapplySeq[T](x : scala.Array[T]) : scala.Option[scala.IndexedSeq[T]] = { /* compiled code */ }
```
`unapplySeq`返回一个序列即数组中的值以和模式匹配case语句中的表达式进行对应位置的值匹配
`unapplySeq`返回一个序列,包含数组中的所有值,这样在模式匹配时,才能知道对应位置上的值
@ -138,7 +138,7 @@ case class Student(name: String, age: Int) extends Person {}
- 构造器中每个参数都默认为`val`
- 自动地生成`equals, hashCode, toString, copy`等方法;
- 伴生对象中自动生成`apply`方法使得不用new关键字就能构造出相应的对象
- 伴生对象中自动生成`apply`方法,使得可以不用new关键字就能构造出相应的对象
- 伴生对象中自动生成`unapply`方法,以支持模式匹配。
除了上面的特征外,样例类和其他类相同,可以任意添加方法和字段,扩展它们。

View File

@ -17,7 +17,7 @@
## 一、初识类和对象
Scala中的类与Java中的类具有非常多的相似性这里为了有个直观上的印象首先给出一个类的示例。
Scala的类与Java的类具有非常多的相似性示例如下
```scala
// 1. 在scala中类不需要用public声明,所有的类都具有公共的可见性
@ -26,11 +26,11 @@ class Person {
// 2. 声明私有变量,用var修饰的变量默认拥有getter/setter属性
private var age = 0
// 3.如果声明的变量不需要进行初始赋值,此时scala无法进行类型推荐需要显式指明类型
// 3.如果声明的变量不需要进行初始赋值,此时Scala就无法进行类型推断所以需要显式指明类型
private var name: String = _
// 4. 定义方法,应该指明传参类型和返回值的类型
// 4. 定义方法,应指明传参类型。返回值类型不是必须的Scala可以自动推断出来但是为了方便调用者建议指明
def growUp(step: Int): Unit = {
age += step
}
@ -58,7 +58,7 @@ class Person {
object Person {
def main(args: Array[String]): Unit = {
// 8.创建类的实例对象
// 8.创建类的实例
val counter = new Person()
// 9.用var修饰的变量默认拥有getter/setter属性可以直接对其进行赋值
counter.age = 12
@ -132,7 +132,7 @@ object Person {
> > javap -private Person
> ```
>
> 编译结果如下从编译结果可以看到实际的get和set的方法名同时也验证了成员变量默认的可见性为public。
> 编译结果如下从编译结果可以看到实际的get和set的方法名(因为JVM不允许在方法名中出现所以它被翻译成$eq)同时也验证了成员变量默认的可见性为public。
>
> ```java
> Compiled from "Person.scala"
@ -172,7 +172,7 @@ object Person {
### 2.4 主构造器
和Java不同的是Scala类的主构造器直接写在类名后面同时需要注意以下两点:
和Java不同的是Scala类的主构造器直接写在类名后面注意以下两点:
+ 主构造器传入的参数默认就是val类型的即不可变你没有办法在内部改变传参
+ 写在主构造器中的代码块会在类初始化的时候被执行功能类似于Java的静态代码块`static{}`
@ -206,7 +206,7 @@ heibaiying:20
辅助构造器有两点硬性要求:
+ 辅助构造器的名称必须为this;
+ 辅助构造器的名称必须为this
+ 每个辅助构造器必须以主构造器或其他的辅助构造器的调用开始。
```scala
@ -236,7 +236,7 @@ object Person {
### 2.6 方法传参不可变
在Scala中方法传参默认是val类型即不可变这意味着你在方法体内部不能改变传入的参数。这和scala的设计理念有关Scala遵循函数式编程理念强调方法不应该有副作用。
在Scala中方法传参默认是val类型即不可变这意味着你在方法体内部不能改变传入的参数。这和Scala的设计理念有关Scala遵循函数式编程理念强调方法不应该有副作用。
```scala
class Person() {
@ -378,16 +378,19 @@ object Color extends Enumeration {
使用枚举类:
```scala
// 1.使用类型别名导入枚举类
import com.heibaiying.Color.Color
object ScalaApp extends App {
// 1.使用枚举类型,这种情况下需要导入枚举类,在枚举类中定义的类型别名就有用了
// 2.使用枚举类型,这种情况下需要导入枚举类
def printColor(color: Color): Unit = {
println(color.toString)
}
// 2.判断传入值和枚举值是否相等
// 3.判断传入值和枚举值是否相等
println(Color.YELLOW.toString == "yellow")
// 3.遍历枚举类和值
// 4.遍历枚举类和值
for (c <- Color.values) println(c.id + ":" + c.toString)
}

View File

@ -60,9 +60,7 @@ object Utils {
### 2.1 类型上界限定
Scala和Java一样对于对象之间进行大小比较要求被比较的对象实现`java.lang.Comparable`接口。
所以如果想对泛型进行比较,需要限定类型上界为`java.lang.Comparable`,语法为` S <: T`,代表类型S是类型T的子类或其本身。示例如下
Scala和Java一样对于对象之间进行大小比较要求被比较的对象实现`java.lang.Comparable`接口。所以如果想对泛型进行比较,需要限定类型上界为`java.lang.Comparable`,语法为` S <: T`代表类型S是类型T的子类或其本身。示例如下
```scala
// 使用 <: 符号限定T必须是Comparable[T]的子类型
@ -126,7 +124,7 @@ trait Ordered[A] extends Any with java.lang.Comparable[A] {
<div align="center"> <img src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/scala-richInt.png"/> </div>
要想解决传入数值无法进行比较的问题,可以使用视图界定。语法为`T <% U`,代表T能够通过隐式转换转为U即允许Int型参数在无法进行比较的时候转换为RichInt类型。示例如下
要想解决传入数值无法进行比较的问题,可以使用视图界定。语法为`T <% U`代表T能够通过隐式转换转为U即允许Int型参数在无法进行比较的时候转换为RichInt类型。示例如下
```scala
// 视图界定符号 <%
@ -147,7 +145,7 @@ class Pair[T <% Comparable[T]](val first: T, val second: T) {
### 2.3 类型约束
如果你用的Scala是2.11+,会发现IDEA会提示视图界定已被标识为废弃。官方推荐使用类型约束(type constraint)来实现同样的功能,其本质是使用隐式参数进行隐式转换,示例如下:
如果你用的Scala是2.11+,会发现视图界定已被标识为废弃。官方推荐使用类型约束(type constraint)来实现同样的功能,其本质是使用隐式参数进行隐式转换,示例如下:
```scala
// 1.使用隐式参数隐式转换为Comparable[T]
@ -224,7 +222,7 @@ object ScalaApp extends App {
}
```
Scala针对这个问题提供了ClassTag上下文界定即把泛型的信息存储在ClassTag中这样在运行阶段需要时只需要从中进行获取即可。其语法为`T : ClassTag`,示例如下:
Scala针对这个问题提供了ClassTag上下文界定即把泛型的信息存储在ClassTag中这样在运行阶段需要时只需要从ClassTag中进行获取即可。其语法为`T : ClassTag`,示例如下:
```scala
import scala.reflect._
@ -309,7 +307,7 @@ object ScalaApp extends App {
### 3.1 Comparable
```scala
```java
import java.util.Arrays;
// 实现Comparable接口
public class Person implements Comparable<Person> {
@ -342,7 +340,7 @@ ying:77
### 3.2 Comparator
```scala
```java
import java.util.Arrays;
import java.util.Comparator;

View File

@ -32,7 +32,7 @@ Scala中继承关系如下图
### 1.2 extends & override
Scala的集成机制和Java有很多相似之处比如都使用`extends`关键字表示继承,都使用`override`关键字表示重写父类的方法或成员变量。下面给出一个Scala继承的示例:
Scala的集成机制和Java有很多相似之处比如都使用`extends`关键字表示继承,都使用`override`关键字表示重写父类的方法或成员变量。示例如下
```scala
//父类
@ -102,7 +102,7 @@ class Employee(name:String,age:Int,salary:Double) extends Person(name:String,age
### 1.4 类型检查和转换
想要实现类检查可以使用`isInstanceOf`,判断一个实例是否来源于某个类或者其子类,如果是,则可以使用`asInstanceOf`进行强制类型转换。
想要实现类检查可以使用`isInstanceOf`判断一个实例是否来源于某个类或者其子类,如果是,则可以使用`asInstanceOf`进行强制类型转换。
```scala
object ScalaApp extends App {
@ -208,7 +208,7 @@ class Employee extends {
但是这种语法也有其限制:你只能在上面代码块中重写已有的变量,而不能定义新的变量和方法,定义新的变量和方法只能写在下面代码块中。
>**注意事项**不仅是类的继承存在这个问题,后文介绍的特质(trait)的继承也存在这个问题,也同样可以通过提前定义来解决。即便可以通过多种方法解决该问题,但还是建议合理设计继承以规避此类问题。
>**注意事项**类的继承和下文特质(trait)的继承都存在这个问题,也同样可以通过提前定义来解决。虽然如此,但还是建议合理设计以规避该类问题。
<br/>
@ -270,7 +270,7 @@ trait Logger {
}
```
想要使用特质,需要使用`extends`关键字,而不是`implements`关键字,如果想要添加多个特质,可以使用`with`关键字。
想要使用特质,需要使用`extends`关键字而不是`implements`关键字,如果想要添加多个特质,可以使用`with`关键字。
```scala
// 1.使用extends关键字,而不是implements,如果想要添加多个特质可以使用with关键字
@ -310,7 +310,7 @@ class InfoLogger extends Logger {
### 3.3 带有特质的对象
Scala支持在类定义的时混入`父类trait`,而在类实例化为具体对象的时候指明其实际使用的`子类trait`下面给出一个示例:
Scala支持在类定义的时混入`父类trait`,而在类实例化为具体对象的时候指明其实际使用的`子类trait`。示例如下
<div align="center"> <img src="https://github.com/heibaiying/BigData-Notes/blob/master/pictures/scala带有特质的对象.png"/> </div>
@ -388,9 +388,9 @@ object ScalaApp extends App {
class Employee extends Person with InfoLogger with ErrorLogger {...}
```
1. 超类首先被构造(Person构造器执行)
1. 超类首先被构造即Person的构造器首先被执行
2. 特质的构造器在超类构造器之前,在类构造器之后;特质由左到右被构造;每个特质中,父特质首先被构造;
+ Logger构造器执行(Logger是InfoLogger的父类)
+ Logger构造器执行Logger是InfoLogger的父类
+ InfoLogger构造器执行
+ ErrorLogger构造器执行;
3. 所有超类和特质构造完毕,子类才会被构造。

View File

@ -129,7 +129,7 @@ This is Class B
上面我们使用的方法相当于直接定义在执行代码的作用域中,下面分别给出其他两种定义的代码示例:
定义在原类型的伴生对象中:
**定义在原类型的伴生对象中**
```scala
class Person(val name: String)
@ -154,7 +154,7 @@ object ScalaApp extends App {
}
```
定义在一个公共的对象中:
**定义在一个公共的对象中**
```scala
object Convert {
@ -167,12 +167,11 @@ object Convert {
import com.heibaiying.Convert._
object ScalaApp extends App {
// 这样普通人也能举起雷神之锤
new Person("普通人").hammer()
}
```
> 注Scala隐式转换函数大部分定义在`Predef.scala`你可以打开源文件查看也可以在Scala交互式命令行中采用`:implicit -v`查看全部隐式转换函数。
> 注Scala自身的隐式转换函数大部分定义在`Predef.scala`你可以打开源文件查看也可以在Scala交互式命令行中采用`:implicit -v`查看全部隐式转换函数。
<br/>
@ -180,7 +179,7 @@ object ScalaApp extends App {
### 2.1 使用隐式参数
函数或方法可以带有一个标记为`implicit`的参数列表,这种情况下,编译器将会查找默认值,提供给函数调用。
在定义函数或方法时可以使用标记为`implicit`的参数,这种情况下,编译器将会查找默认值,提供给函数调用。
```scala
// 定义分隔符类
@ -243,7 +242,7 @@ formatted("this is context")
我们上面示例程序相当于直接定义执行代码的上下文作用域中,下面给出其他两种方式的示例:
定义在隐式参数对应类的伴生对象中;
**定义在隐式参数对应类的伴生对象中**
```scala
class Delimiters(val left: String, val right: String)
@ -264,7 +263,7 @@ object ScalaApp extends App {
}
```
统一定义在一个文件中,在使用时候导入:
**统一定义在一个文件中,在使用时候导入**
```scala
object Convert {
@ -292,9 +291,7 @@ object ScalaApp extends App {
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中如果定义了一个如上所示的比较对象大小的泛型方法你会发现无法通过编译。对于对象之间进行大小比较Scala和Java一样都要求被比较的对象需要实现java.lang.Comparable接口。在Scala中直接继承Java中Comparable接口的是特质Ordered它在继承compareTo方法的基础上额外定义了关系符方法源码如下:
```scala
trait Ordered[A] extends Any with java.lang.Comparable[A] {
@ -336,7 +333,7 @@ object Pair extends App {
#### 2. 利用隐式参数进行隐式转换
Scala2.11+后,视图界定被标识为废弃,官方推荐使用类型限定来解决上面的问题,本质上就是使用隐式参数进行隐式转换。
Scala2.11+后,视图界定被标识为废弃,官方推荐使用类型限定来解决上面的问题,本质上就是使用隐式参数进行隐式转换。
```scala
object Pair extends App {