diff --git a/README.md b/README.md index 4cad8ba..d760e2d 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/notes/Scala函数和闭包.md b/notes/Scala函数和闭包.md index 5f0b5ec..f492a36 100644 --- a/notes/Scala函数和闭包.md +++ b/notes/Scala函数和闭包.md @@ -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`的值。 想要获得柯里化的中间返回的函数其实也比较简单: diff --git a/notes/Scala列表和集.md b/notes/Scala列表和集.md index 705c1af..e33ade7 100644 --- a/notes/Scala列表和集.md +++ b/notes/Scala列表和集.md @@ -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的字符串表现形式,可以使用mkString,mkString有三个重载方法: +如果想改变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 ) diff --git a/notes/Scala模式匹配.md b/notes/Scala模式匹配.md index f43951f..1b06f8c 100644 --- a/notes/Scala模式匹配.md +++ b/notes/Scala模式匹配.md @@ -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`方法,以支持模式匹配。 除了上面的特征外,样例类和其他类相同,可以任意添加方法和字段,扩展它们。 diff --git a/notes/Scala类和对象.md b/notes/Scala类和对象.md index df80bd5..d1e3a0d 100644 --- a/notes/Scala类和对象.md +++ b/notes/Scala类和对象.md @@ -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 @@ -120,8 +120,8 @@ object Person { > > ```scala > class Person { -> var name = "" -> private var age = "" +> var name = "" +> private var age = "" > } > ``` > @@ -132,21 +132,21 @@ object Person { > > javap -private Person > ``` > -> 编译结果如下,从编译结果可以看到实际的get和set的方法名,同时也验证了成员变量默认的可见性为public。 +> 编译结果如下,从编译结果可以看到实际的get和set的方法名(因为JVM不允许在方法名中出现=,所以它被翻译成$eq),同时也验证了成员变量默认的可见性为public。 > > ```java > Compiled from "Person.scala" > public class Person { -> private java.lang.String name; -> private java.lang.String age; -> -> public java.lang.String name(); -> public void name_$eq(java.lang.String); -> -> private java.lang.String age(); -> private void age_$eq(java.lang.String); -> -> public Person(); +> private java.lang.String name; +> private java.lang.String age; +> +> public java.lang.String name(); +> public void name_$eq(java.lang.String); +> +> private java.lang.String age(); +> private void age_$eq(java.lang.String); +> +> public Person(); > } > ``` @@ -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) } diff --git a/notes/Scala类型参数.md b/notes/Scala类型参数.md index 9e6bae4..59f05a4 100644 --- a/notes/Scala类型参数.md +++ b/notes/Scala类型参数.md @@ -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] {
-要想解决传入数值无法进行比较的问题,可以使用视图界定。语法为`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 { @@ -342,7 +340,7 @@ ying:77 ### 3.2 Comparator -```scala +```java import java.util.Arrays; import java.util.Comparator; @@ -358,7 +356,7 @@ public class Person { public static void main(String[] args) { Person[] peoples= {new Person("hei", 66), new Person("bai", 55), new Person("ying", 77)}; // 这里为了直观直接使用匿名内部类,实现Comparator接口 - //如果是Java8你也可以写成Arrays.sort(peoples, Comparator.comparingInt(o -> o.age)); + //如果是Java8你也可以写成Arrays.sort(peoples, Comparator.comparingInt(o -> o.age)); Arrays.sort(peoples, new Comparator() { @Override public int compare(Person o1, Person o2) { diff --git a/notes/Scala继承和特质.md b/notes/Scala继承和特质.md index 14e46c4..b3556bb 100644 --- a/notes/Scala继承和特质.md +++ b/notes/Scala继承和特质.md @@ -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)的继承都存在这个问题,也同样可以通过提前定义来解决。虽然如此,但还是建议合理设计以规避该类问题。
@@ -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`。示例如下:
@@ -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. 所有超类和特质构造完毕,子类才会被构造。 diff --git a/notes/Scala隐式转换和隐式参数.md b/notes/Scala隐式转换和隐式参数.md index 56a8e35..8627591 100644 --- a/notes/Scala隐式转换和隐式参数.md +++ b/notes/Scala隐式转换和隐式参数.md @@ -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`查看全部隐式转换函数。
@@ -180,7 +179,7 @@ object ScalaApp extends App { ### 2.1 使用隐式参数 -函数或方法可以带有一个标记为`implicit`的参数列表,这种情况下,编译器将会查找默认值,提供给函数调用。 +在定义函数或方法时可以使用标记为`implicit`的参数,这种情况下,编译器将会查找默认值,提供给函数调用。 ```scala // 定义分隔符类 @@ -204,7 +203,7 @@ object ScalaApp extends App { 1.我们上面定义`formatted`函数的时候使用了柯里化,如果你不使用柯里化表达式,按照通常习惯只有下面两种写法: ```scala -//这种写法没有语法错误,但是无法通过编译 +// 这种写法没有语法错误,但是无法通过编译 def formatted(implicit context: String, deli: Delimiters): Unit = { println(deli.left + context + deli.right) } @@ -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 {