Scala学习教程笔记三之函数式编程、集合操作、模式匹配、类型参数、隐式转换、Actor、

时间:2023-11-22 13:25:14

1:Scala和Java的对比:

1.1:Scala中的函数是Java中完全没有的概念。因为Java是完全面向对象的编程语言,没有任何面向过程编程语言的特性,因此Java中的一等公民是类和对象,而且只有方法的概念,即寄存和依赖于类与对象中的方法。Java中的方法是绝对不可能脱离类和对象独立存在的。
1.2:Scala是一门既面向对象,又面向过程的语言。因此在Scala中有非常好的面向对象的特性,可以使用Scala来基于面向对象的思想开发大型复杂的系统和工程,而且Scala也面向过程,因此Scala中有函数的概念。在Scala中,函数和类,对象等一样,都是一等公民。Scala中的函数可以独立存在,不需要依赖于任何类与对象。
1.3:Scala的函数式编程,就是Scala面向过程的最好的佐证。也正是因为函数式编程,才让Scala具备了Java所不具备对的更强大的功能和特性。
而之所以Scala一直没有替换Java,是因为Scala之前一直没有开发过太多知名的应用。而Java则不一样。最重要的一点在于,Java现在不只是一门编程语言,还是一个庞大的,涵盖了软件开发,甚至云计算,大数据的技术生态。其中重要框架和系统包含Spring,lucene,Hadoop,Activiti等等。

2:Scala之函数式编程:

2.1:将函数赋值给变量:
    Scala中的函数是一等公民,可以独立定义,独立存在,而且可以直接将函数作为值赋值给变量,必须在函数后面加上空格和下划线:
    class Person{       def hello(name : String) = {
        println("hi," + name)
      }
      //将函数赋值给变量的语法,函数命令 _(1、函数名称,2、空格,3、下划线)
      val sayHello = hello _;
      sayHello("张三");
    }
    //定义一个Object来测试
    object Object {       def main(args: Array[String]): Unit = {
        val p1 = new Person;
        p1.sayHello
      }
    }
2.2:匿名函数:
    Scala中,函数也可以不需要命名,此时函数被称为匿名函数。可以直接定义函数之后,将函数赋值给某个变量,也可以将直接定义的匿名函数传入其他函数之中。Scala定义匿名函数的语法规则就是:(参数名称:参数类型) => 函数体;这种语法必须掌握和深刻理解。
    class Person{       val sayHello = (name : String) => println("hi,"+ name);
    }
    //定义一个Object来测试
    object Object {       def main(args: Array[String]): Unit = {
        val p1 = new Person;
        p1.sayHello("张三");
      }
    }
2.3:高阶函数:
    Scala中,由于函数是一等公民,因此可以直接将某个函数传入其他函数,作为参数。这个功能是及其强大的,也是Java这种面向对象的编程语言所不具备的。
    接受其他函数作为参数的函数,也被称作为高阶函数(higher-order function),高阶函数的另一个功能就是将函数作为返回值:
    class Person{       val sayHello = (name : String) => println("hi,"+ name);
      //def 函数名称(函数名称:(函数类型) => 函数返回值,参数1 :参数类型......)
      def greeting(hello:(String) => Unit, name: String): Unit ={
        hello(name);
      }
    }
    //定义一个Object来测试        
    object Object {       def main(args: Array[String]): Unit = {
        val p1 = new Person;
        p1.greeting(p1.sayHello, "李思思");
      }
    }
    //高阶函数的另一个功能就是将函数作为返回值:
    class Person{
      //高阶函数的另一个功能就是将函数作为返回值:
      def getGreetingFunc(msg : String) = (name : String) => println(msg +", " + name);
      val greetingFunc = getGreetingFunc("hello");
      greetingFunc("张三三");
    }
    //定义一个Object来测试
    object Object {       def main(args: Array[String]): Unit = {
        val p1 = new Person;
        p1.greetingFunc;
        println(Array(1,2,3,4,5).map((num : Int) => num * num).toBuffer)
      }
    }
2.4:高阶函数的类型推断:
    高阶函数可以自动推断出参数类型,而不需要写明类型,而且对于只有一个参数的函数,还可以省去其小括号。如果仅有一个参数在右侧的函数体内只使用一次,则还可以将接受参数省略,并且将参数用_来替代。
    class Person{       //func函数名称作为参数一。name是参数二
      def greeting(func:(String) => Unit,name : String) {
        func(name)
      }
      //匿名函数
      greeting((name : String) => println("hello," + name) ,"张三");
      //高阶函数可以自动推断出参数类型,而不需要写明类型。如这里没有写name的String类型。
      greeting((name) => println("hello, "+ name) ,"李思思");
      //而且对于只有一个参数的函数,还可以省去其小括号。
      greeting(name => println("hello," + name) ,"王五五");       //如果仅有一个参数在右侧的函数体内只使用一次,则还可以将接受参数省略,并且将参数用_来替代。
      def triple(func:(Int) =>Int) ={
        func(3)
      }     }
    //定义一个Object来测试
    object Object {       def main(args: Array[String]): Unit = {
        val p1 = new Person;
        println(p1.triple(10 * _))
      }
    }
2.5:Scala的常用的高阶函数:
    a、map,对传入的每个元素都进行映射,返回一个处理后的元素:
        println(Array(1,2,3,4,5).map(2 * _).toBuffer);
    b、foreach,对传入的每个元素都进行处理,但是没有返回值:
        (1 to 9).map("*" * _).foreach(println _);
    c、filter,对传入的每个元素都进行条件判断,如果对元素返回true,则保留该元素,否则过滤掉该元素:
        val a = (1 to 20).filter(_ % 2 ==0);
        println(a)
    d、reduceLeft,从左侧元素开始,进行reduce操作,即先对元素1和元素2进行处理,然后将结果与元素3处理,再将结果与元素4处理,依次类推,即为reduce:
        var a = (1 to 9).reduceLeft(_ * _);
        //此操作相当于1*2*3*4*5*6*7*8*9
        println(a)
    e、sortWith,对元素进行两两相比,进行排序:
        var arr = Array(3,2,4,1,5,7).sortWith(_ < _);
        println(arr.toBuffer)
2.6:闭包:
    a、在Java中,不支持直接将函数传入一个方法作为参数,通常来说,唯一的方法就是定义一个实现了某个接口的类的实例对象,该对象只有一个方法。而这些接口都只有单个的抽象方法,也就是single abstract method,简称为SAM;
    b、由于Scala是可以调用Java的代码的,因此当我们调用Java的某个方法的时候,可能就不得不创建SAM传递给方法,非常麻烦。但是Scala又是直接传递函数的。此时就可以使用Scala提供的,在调用Java方法的时候,使用的功能。SAM转换,即将SAM转换为Scala函数。(注意:要使用SAM转换为,需要使用Scala提供的特性,隐式转换):
    c、闭包,最简洁的解释就是函数在变量不处于其有效作用域的时候,还能够对变量进行访问,即为闭包;
        class Person{           def getGreetingFunc(msg : String) = (name : String) => println(msg + "," + name);
          //两次调用getGreetingFunc函数,传入不同的msg,并创建不同的函数返回.
          //然而,msg只是一个局部变量,却在getGreetingFunc执行完以后,还可以继承存在创建的函数之中。
          //greetingFuncHello("张三"),调用的时候,值为"hello"的msg被保留在了函数体内部,可以反复的使用。
          //这种变量超出了其作用域,还可以使用的情况,即为闭包。           val greetingFuncHello = getGreetingFunc("hello");
          val greetingFuncHi = getGreetingFunc("hi");
          //Scala通过为每个函数创建对象来实现闭包,实际上对于getGreetingFunc函数创建的函数。msg是作为函数对象的变量存在的。因此每个函数才可以拥有不同的msg
          //Scala编译器会确保上述闭包机制。         }
        //定义一个Object来测试
        object Object {           def main(args: Array[String]): Unit = {
            val p1 = new Person;
            p1.greetingFuncHello("张三三");
            p1.greetingFuncHi("李思思");
          }
        }
2.7:SAM转换(Scala函数和Java的类转换):
  a、在Java中,不支持直接将函数传入一个方法作为参数,通常来说,唯一的方法就是定义一个实现了某个接口的类的实例对象,该对象只有一个方法。而这些接口都只有单个的抽象方法,也就是single abstract method,简称为SAM;
   b、由于Scala是可以调用Java的代码的,因此当我们调用Java的某个方法的时候,可能就不得不创建SAM传递给方法,非常麻烦。但是Scala又是直接传递函数的。此时就可以使用Scala提供的,在调用Java方法的时候,使用的功能。SAM转换,即将SAM转换为Scala函数。(注意:要使用SAM转换为,需要使用Scala提供的特性,隐式转换):
2.8:Currying函数:
  Currying函数指的是,将原来接受两个参数的一个函数,转换为两个函数,第一个函数接受原先的第一个参数,然后返回接受原先第二个参数的第二个函数。在函数调用的过程中,就变为了两个函数连续调用的形式:
    class Person{       def sum(a: Int ,b : Int) = a + b;
      def sum2(a : Int) = (b : Int) => a+b;
      def sum3(a : Int)(b : Int) = a + b;
    }
    //定义一个Object来测试
    object Object {       def main(args: Array[String]): Unit = {
        val p = new Person;
        println(p.sum(1,4));
        println(p.sum2(2)(2));
        println(p.sum3(3)(3));
      }
    }
2.9:return:
  Scala中,不需要使用return来返回函数的值,函数最后一行语句的值,就是函数的返回值。在Scala中,return用于在匿名函数中返回值给包含匿名函数的带名函数,并作为带名函数的返回值。使用return的匿名函数,是必须给出返回类型的,否则无法通过编译。
    class Person{       def greeting(name : String) ={
        def hello(name : String): String ={
           return "hello," + name;
        }
        hello(name);
      }
    }        
    //定义一个Object来测试    
    object Object {       def main(args: Array[String]): Unit = {
        val p = new Person;
        println(p.greeting("李思思"));
      }
    }

3:Scala函数式编程之集合操作:

3.1:Scala的集合体系结构:
Scala中的集合体系主要包括:Iterable,Seq,Set,Map。其中,Iterable是所有集合trait的跟trait。这个结构与Java的集合体系非常相似。
Scala中的集合是分成可变和不可变的两类集合的,其中可变集合就是说,集合的元素可以动态修改。而不可变集合的元素在初始化之后,就无法修改了。分别对应scala.collection.mutable和scala.collection.immutable两个包。
Scala中的Seq下包含了Range,ArrayBuffer,List等子trait。其中Range就代表了一个序列,通常可以使用"1 to 10"这种语法来产生一个Range。ArrayBuffer就类似于Java中的ArrayList。
Scala的集合类的map,flatMap,reduce,reduceLeft,foreach,等这些函数就是高阶函数。可以接受其他函数作为参数。
3.2:List:
List代表一个不可变的列表,List的创建,val list = new List(,,,);List有head和tail,head代表List的第一个元素,tail代表第一个元素以后的所有元素。list.head,list.tail;
List有特殊的::操作符,可以用于将head和tail合并成一个List,::list; 3.3:LinkedList:
LinkedList代表一个可变的列表,使用elem可以引用其头部,使用next可以引用其尾部。
val l = mutable.LinkedList(,,,,,,);
println(l.elem + " " + l.next);
3.4:Set:
Set代表一个没有重复元素的集合。将重复元素加入Set是没有用的。而且Set是不保证插入顺序的,也就是说,Set中的元素是乱序的。LinkedHashSet会用一个链表来维护插入顺序。SortedSet会自动根据key来进行排序的。 3.5:集合的函数式编程:
a、map案例,为List中每个元素都添加一个前缀:
val list = List("张三","李思思","王五五").map("name is :" + _);
println(list.toBuffer)
b、faltMap案例,将List中的多行句子拆分成单词:
val list = List("hello world , i am bie xiansheng").flatMap(_.split(" "));
println(list.toBuffer)
c、foreach案例,打印List中的每个单词:
List("hello world , i am bie xiansheng").foreach(println(_));
d、zip案例,对学生姓名和学生成绩进行关联:
val list = List("张三三","李思思","王五五","赵六六").zip(List("","","",""));
println(list)

4:Scala之模式匹配(mathch case语法,即为模式匹配):

模式匹配是Scala中非常有特色的,非常强大的一种功能。模式匹配,其实类似于Java中的swich case语法,即对一个值进行条件判断,然后针对某种条件,即针对不同的处理。但是Scala的模式匹配功能比Java的swich case语法的功能强大的多,Java的swich case语法只能对值进行匹配。但是Scala的模式匹配除了可以对值进行匹配之外,还可以对类型进行匹配,对Array和List的元素情况进行匹配,对case class进行匹配,甚至对有值或者没值进行匹配。

4.1:模式匹配的基础语法:
a、mathch case的语法如下所示:变量 mathch {case 值 => 语法}。如果值为下划线,则代表了不满足以上所有情况下的默认情况如何处理。此外,match case中,主要一个case分支满足并处理了,就不会继续判断下一个case的分支了。(区别Java的break;)
object Object { def main(args: Array[String]): Unit = {
def judgeGeade(grade : String): Unit ={
grade match {
case "A" => println("A");
case "B" => println("B");
case "C" => println("C");
case "D" => println("D");
case _ => println("没有默认匹配的")
}
}
judgeGeade("A");
judgeGeade("E");
}
}
b、模式匹配中使用if守卫:
Scala的模式匹配语法,有一个特点在于,可以在case后的条件判断中,不仅仅只是提供一个值,而是可以在值的后面再加一个if守卫,进行双重过滤:
object Object { def main(args: Array[String]): Unit = {
def judgeGeade(name : String ,grade : String): Unit ={
grade match {
case "A" => println(name + "A");
case "B" => println(name +" B");
case "C" => println(name +" C");
case "D" => println(name +" D");
case _ if name == "张三三" => println(name + ",you are luck boy!");
case _ => println("没有默认匹配的")
}
}
judgeGeade("张三三","A");
}
}
c、在模式匹配中进行变量赋值:
Scala的模式匹配语法中,有一个特点在于,可以将模式匹配的默认情况,下划线,替换为一个变量名,此时模式匹配语法就会将要匹配的值赋值给这个变量,从而可以在后面的处理语句中使用要匹配的值:
优点:可以显示出_grade的值;
object Object { def main(args: Array[String]): Unit = {
def judgeGeade(name : String ,grade : String): Unit ={
grade match {
case "A" => println(name + "A");
case "B" => println(name +" B");
case "C" => println(name +" C");
case "D" => println(name +" D");
case _grade if name == "张三三" => println(name + ",you are luck boy!" + _grade);
case _ => println("没有默认匹配的")
}
}
judgeGeade("张三三","E");
}
}
4.2:对类型进行模式匹配:
Scala的模式匹配一个强大之处就在于,可以直接匹配类型,而不是值。
object Object { def main(args: Array[String]): Unit = {
def processException(e : Exception): Unit ={
e match {
case e1 : IllegalArgumentException => println("IllegalArgumentException" + e1);
case e2 : FileNotFoundException => println("FileNotFoundException" + e2);
case e3 : IndexOutOfBoundsException => println("IndexOutOfBoundsException" + e3);
case _ : Exception => println("Exception");
}
}
processException(new IllegalArgumentException());
}
}
4.3:对Array和List的元素进行模式匹配:
对Array进行模式匹配,分别可以匹配带有指定元素的数组,带有指定个数元素的数组,以某元素打头的数组。
对List进行模式匹配,与Array类似,但是需要使用List特有的::操作符;
object Object { def main(args: Array[String]): Unit = {
def greeting(arr : Array[String]): Unit ={
arr match {
case Array("张三三") => println("hi,张三三");
case Array(a,b,c,d) => println("hi," + a +" ,"+ b + ", " + c + " ,"+ d);
case Array("李思思",_*) => println("hi,李思思");
case _ => println("hi,how are you.")
}
}
greeting(Array("张三三","李思思","王五五","a","b","c","d"));
greeting(Array("李思思"));
greeting(Array("a","b","c","d"));
}
}
//使用List来进行模式匹配;
object Object { def main(args: Array[String]): Unit = {
def greeting(list : List[String]): Unit ={
list match {
case "张三三" :: Nil => println("hi,张三三");
case a :: b :: c :: d => println("hi,"+ a +" ,"+ b + ", " + c + " ,"+ d);
case "李思思" :: tail => println("hi,李思思");
case _ => println("hi,how are you.")
}
}
greeting(List("张三三"));
greeting(List("李思思"));
greeting(List("王五五")); }
}
4.4:case class与模式匹配:
Scala中提供了一种特殊的类,用case class进行声明,中文也可以称做样例类。case class其实有点类似于Java中的JavaBean的概念。即只定义field,并且由Scala编译时自动提供getter和setter方法,但是没有method。case calss的主构造函数接受的参数通常不需要使用var或者val修饰,scala自动就会使用val修饰,但是如果你自己使用var修饰,name还是会按照var来。Scala自动为case class定义了伴生对象,也就是Obeject,并且定义了apply()方法,该方法接受主构造函数中相同的参数,并且返回case class对象。
case class Student(name : String,classroom : String) extends Person{ }
case class Teacher(name : String,subject : String) extends Person { }
class Person{ def judge(p : Person): Unit ={
p match {
case Teacher(name,subject) => println("teacher ,name is: "+ name +",subject is :" +subject)
case Student(name,classroom) => println("student,name is: " + name +", classroot is :" + classroom);
case _ => println("illegal access,please go out of the school")
}
}
}
//定义一个Object
object Object { def main(args: Array[String]): Unit = {
val zhangsan : Person = Teacher("张三三","语文");
val lisisi : Person = Teacher("李思思","数学"); val wangwu : Person = Student("王五五","一年级");
val person = new Person;
person.judge(zhangsan);
person.judge(lisisi);
person.judge(wangwu);
}
} 4.5:Option与模式匹配:
Scala有一种特殊的类型,叫做Option。Option有两种值,一种是Some,表示有值,一种是None,表示没有值。Option通常会用于模式匹配中,用于判断某个变量是有值还是没有值,这比null来的更加简明。
class Person{ val grades = Map("张三三" -> "A" ,"李思思" -> "B","王五五" -> "C");
def getGrade(name :String): Unit ={
val grade = grades.get(name);
grade match {
case Some(grade) => println("you grade is : " + grade);
case None => println("sorry,you grade is null")
}
}
}
//定义一个Object
object Object { def main(args: Array[String]): Unit = {
val p = new Person;
p.getGrade("张三三");
p.getGrade("李思思");
p.getGrade("王五五");
p.getGrade("赵六六");
}
}

5:Scala中的类型参数:

其实就是类似于Java中的泛型。也是定义一种类型参数,比如在集合,在类,在函数中,定义类型参数,然后就可以保证使用到该类型参数的地方,就肯定,也只能是这种类型。从而实现程序更好的健壮性。

5.1:泛型类:
顾名思义,其实就是在类的声明中,定义一些泛型类型,然后在类内部,比如field或者method,就可以使用这些泛型类型。使用泛型类,通常是需要对类中的某些成员,比如某些field或者method中的参数或者变量,进行统一的类型限制,这样可以保证程序更好的健壮性和稳定性。如果不使用泛型进行统一的类型限制,那么在后期程序运行过程中,难免出现问题,比如传入了不希望的类型,导致程序出现问题。在使用类的时候,比如创建类的对象,将类型参数替换为实际的类型,即可。或者直接给使用了泛型类型的field赋值时候,Scala会自动进行类型推断。
class Person[T](val id : T){ def getMessage(name : T) ={
println("id:" + id + ",name:" + name);
}
}
//定义一个Object进行测试
object Object { def main(args: Array[String]): Unit = {
val p = new Person[String]("");
p.getMessage("张三三");
}
}
5.2:泛型函数:
泛型函数,与泛型类类似,可以给某个函数在声明的时候指定泛型类型,然后再函数体内,多个变量或者返回值之间,就可以使用泛型类型进行声明,从而对某个特殊的变量,或者多个变量,进行强制性的类型限制。于泛型类一样,你可以通过给使用了泛型类型的变量传递值来让Scala自动推断泛型的实际类型,也可以在调用函数时候,手动指定泛型类型。
class Person{ def getCard[T](content : T)={
if(content.isInstanceOf[Int]){
println("card:001:" + content );
}else if(content.isInstanceOf[String]){
println("card:002:" + content);
}else{
println("card:003:" + content)
}
}
}
//定义一个Object进行测试
object Object { def main(args: Array[String]): Unit = {
val p = new Person();
p.getCard("张三三");
p.getCard()
p.getCard(true)
}
}
5.3:上边界Bounds:
在指定泛型类型的时候,有时候,我们需要对泛型类型的范围进行界定,而不是可以是任意的。比如,我们可能要求某个泛型类型,它就必须是某个类的子类,这样在程序中就可以放心的调用泛型类型继承的父类的方法,程序才能正常的使用和运行。此时就可以使用上下边界Bounds的特性。Scala的上下边界特性允许泛型类型必须是某个类的子类,或者必须是某个类的父类。
class Person(val name : String){ def hello = println("hi.i am " + name);
def makeFriends(p : Person): Unit ={
hello;
p.hello
}
}
//定义一个Student继承Person
class Student(name : String) extends Person(name){ }
//定义一个类Party使用上边界
class Party[T <: Person](p1 : T,p2 : T) { //def play = p1.makeFriends(p2);
def play(): Unit ={
p1.makeFriends(p2);
}
}
//定义一个Object进行测试
object Object { def main(args: Array[String]): Unit = {
val s1 = new Student("张三三");
val s2 = new Student("李思思"); val p = new Party[Person](s1,s2);
p.play()
}
}
5.4:下边界Bounds:
除了指定泛型类型的上边界,还可以指定下边界,即指定泛型类型必须是某个类的父类;
    class Father(val name : String) {     }
    class Child(name : String) extends Father(name){     }
    //定义一个Object进行测试
    object Object {       def getIdCard[R >: Child](person : R): Unit ={
        if(person.getClass == classOf[Child]){
          println("请告诉我你爸爸的姓名!")
        }else if(person.getClass == classOf[Father]){
          println("请拿走你孩子的身份证!")
        }else{
          println("你开心就好!")
        }
      }
      def main(args: Array[String]): Unit = {
        val c = new Child("张三三");
        getIdCard(c);
        val f = new Father("李思思");
        getIdCard(f)
      }
    }
5.5:View Bounds:
上下边界Bounds,虽然可以让一种泛型类型,支持有父子关系的多种类型。但是,在某个类与上下边界Bounds指定的父子类型范围内的类都没有任何关系,则默认是肯定不能接受的。然而,View Bounds作为一种上下边界Bounds的加强版,支持可以对类型进行隐式转换,将指定的类型进行隐式转换后,再判断是否在边界指定的类型范围内:
    class Person(val name : String) {     }
    class Dog(val name : String) {       def hello(): Unit ={
        println("汪汪汪汪,i am " + name)
      }
    }
    class Patty[T <% Person](p1 :T,p2 : T) {       def show(): Unit ={
        println("View Bounds的练习学习")
      }
    }    
    //定义一个Object进行测试
    object Object {       implicit def dog2Person(dog : Object) : Person ={
        if(dog.isInstanceOf[Dog]){
          val _dog = dog.asInstanceOf[Dog];
          new Person(_dog.name);
        }else{
          null;
        }
      }       def main(args: Array[String]): Unit = {
        val person = new Person("张三三");
        val dog = new Dog("小旺旺");         val party = new Patty[Person](person,dog);
        party.show()
      }
    }
5.6:Context Bounds:
是一种特殊的Bounds,它会根据泛型类型的声明,比如"T : 类型"要求必须存在一个类型为"类型[T]"的隐式值。其实,Context Bounds之所以叫Context,是因为它基于的是一种全局的上下文,需要使用到上下文中的隐式值以及注入。
   
5.7:Manifest Context Bounds:
在Scala中,如果要实例化一个泛型数组,就必须使用Manifest Context Bounds。也就是说,如果数组元素类型为T的话或者函数定义[T :Manifest]泛型类型,这样才能实例化Array[T]这种泛型数组;
    class Meat(val name : String) {     }
    class Vegetable(val name : String) {     }    
    //定义一个Object进行测试
    object Object {       def foodPackage[T : Manifest](food : T*) ={
        val foodPackage = new Array[T](food.length);
        for(i <- 0 until(food.length)){
          foodPackage(i) = food(i);
        }
        foodPackage;
      }
      def main(args: Array[String]): Unit = {
        val m1 = new Meat("大肉");
        val v1 = new Vegetable("胡萝卜");         println(foodPackage(m1,v1).toBuffer);
      }
    }
5.8:协变和逆变:
Scala中的协变和逆变是非常有特色的,解决了Java中的泛型的一大缺憾。比如,Java中,如果有Professional是Master的子类,那么Card[Professional]是不是Card[Master]的子类呢,答案是,肯定不是啊。因此对于发开程序造成了很多的麻烦。而Scala中,只要灵活使用了协变和逆变,就可以解决Java中泛型的问题。
    class Master {     }
    class Professional extends Master{     }
    class Card[+T](val name : String) {
      //+是协变
      def enterMeet(card : Card[Master]): Unit ={
        println("欢迎大师以及大师级别以下的来到会场")
      }
    }
    class Card2[-T](val name : String){       def enterMeet2(card : Card2[Professional]): Unit ={
        println("只有专家级别的才可以进入此会场,如果是大师级别的当然更可以了")
      }
    }
    //定义一个Object进行测试
    object Object {       def main(args: Array[String]): Unit = {
        val p = new Card[Professional]("张三三专家");
        val m = new Card[Master]("李思思大师");
        p.enterMeet(m);
        m.enterMeet(p);         val p2 = new Card2[Professional]("张三三专家");
        val m2 = new Card2[Master]("李思思大师");
        p2.enterMeet2(p2);
        p2.enterMeet2(m2);
        m2.enterMeet2(p2);
        m2.enterMeet2(m2);
      }
    }
5.9:Existential Type:
  在Scala里面,有一种特殊的类型参数,就是    Existential Type,即存在性类型。
    Array[T] forSome{type T}
    Array[_]

6:Scala之隐式转换与隐式参数:

  允许手动指定,将某种类型的对象转换成其他类型的对象。最核心的就是定义隐式转换函数,即implicit conversion function。定义的隐式转换函数,只要在编写的程序内引入,就会被Scala自动使用。Scala会根据隐式转换函数的签名,在程序中使用隐式转换函数接受的参数类型定义的对象时,会自动将其传入隐式转换函数,转换为另外一种类型的对象并且返回。这就是"隐式转换";隐式转换函数的名字随便起,因为通常不会由用户手动调用,而是Scala进行调用的。但是如果要使用隐式转换,则需要对隐式转换函数进行导入。因此通常建议将隐式转换函数的名称命名为"one2one"的形式。

6.1:隐式转换:
实现隐式转换,只要在程序可见的范围内定义隐式转换函数即可。Scala会自动使用隐式转换函数。隐式转换函数与普通函数唯一的语法区别就是,要以implicit开头,而且一定要定义函数返回类型。
class Student(val name : String) { }
class Older(val name : String) { }
class SpecialPerson(val name : String) { var ticketNum = ;
def buySpecialTicket(p : SpecialPerson): Unit ={
ticketNum += ;
println("票数:" + ticketNum);
}
}
//定义一个Object来测试
object Object { implicit def object2SpecialPerson(obj : Object) : SpecialPerson = {
if(obj.getClass == classOf[Student]){
val s = obj.asInstanceOf[Student];
new SpecialPerson(s.name);
}else if(obj.getClass == classOf[Older]){
val older = obj.asInstanceOf[Older];
new SpecialPerson(older.name);
}else{
null;
}
} def main(args: Array[String]): Unit = {
val s = new Student("张三三");
val o = new Older("李思思");
val specialPerson = new SpecialPerson("王五五"); specialPerson.buySpecialTicket(s);
specialPerson.buySpecialTicket(o);
specialPerson.buySpecialTicket(specialPerson);
}
}
6.2:使用隐式转换加强现有类型:
隐式转换非常强大的一个功能,就是可以在不知不觉中加强现有类型的功能。也就是说,可以为,某个类定义一个加强版的类,并定义互相之间的隐式转换,从而让源类在使用加强版的方法时,由Scala自动进行隐式转换为加强肋,然后再调用该方法。
class Man(val name : String) { }
class SuperMan(val name : String) { def emitLaser = println(name + " ,变身超人");
}
//定义一个Object来测试
object Object { implicit def man2SuperMan(man : Man):SuperMan = new SuperMan(man.name); def main(args: Array[String]): Unit = {
val man = new Man("张三三");
man.emitLaser;
}
}
6.3:导入隐式转换函数(隐式转换函数作用域与导入):
Scala默认会使用两种隐式转换,一种是源类型,或者目标类型的伴生对象内的隐式转换函数;一种是当前程序作用域内的可以用唯一标识标识的隐式转换函数。如果隐式转换函数不在上述两种情况下的话,那么就必须手动使用import语法引入某个包下的隐式转换函数,比如import test._。通常建议,仅仅在需要进行隐式转换的地方,比如某个函数或者非法内,用import导入隐式转换函数,这样可以缩小隐式转换函数的作用域,避免不需要的隐式转换。 6.4:隐式转换的发生时机:
a、调用某个函数,但是给函数传入的参数的类型,与函数定义的接受参数类型不匹配。
b、使用某个类型的对象,调用某个方法,而这个方法并不存在与该类型中。
c、使用某个类型的对象,调用某个方法,虽然该类型有这个方法,但是给方法传入的参数类型,与方法定义的接受参数的类型不匹配。
6.5:隐式参数:
所谓的隐式参数,指的是在函数或者方法中,定义一个用implicit修饰的参数,此时Scala会尝试找到一个指定类型的,用implicit修饰的对象,即隐式值,并注入参数。Scala会在两个范围内查找,一种是当前作用域内可见的val或者var定义的隐式变量。一种是隐式参数类型的伴生对象内的隐式值。
class SignPen { def write(context: String) = println(context);
}
object Object { implicit val signPen = new SignPen; def signForExam(name : String)(implicit signPen : SignPen){
signPen.write(name + " come to exam in time;")
}
def main(args: Array[String]): Unit = {
signForExam("张三三");
}
}

7:Scala之Actor(多线程编程,注意,Spark中使用的分布式多线程框架,是Akka):

  Scala的Actor类似于Java中的多线程编程。但是不同的是,Scala的Actor提供的模型与多线程有所不同。Scala的Actor尽可能的避免锁和共享状态,从而避免多线程并发时出现资源争用的情况,进而提示多线程编程的性能。此外,Scala的Actor的这种模型还可以避免死锁等一系列传统多线程编程的问题。

7.1:Actor的创建,启动,和消息的收发:
Scala提供了Actor trait来让我们更方便的进行Actor多线程编程,就Actor trait就类似于Java中的thread和Runnable一样,是基础的多线程基类和接口。我们只要重写Actor trait的act方法,即可实现自己的线程执行体,与Java中重写run方法类似。
此外,使用start()方法启动actor,使用!符号,向actor发送消息。actor内部使用receive和模式匹配接受消息。
class HelloActor extends Actor{ def act(): Unit = {
while(true) {
receive{
case name : String => println("hi," + name)
}
}
}
}
//定义一个Object来进行测试
object Object { def main(args: Array[String]): Unit = {
val helloActor = new HelloActor;
helloActor.start()
}
}
7.2:收发case class类型的消息:
Scala的Actor模型和Java的多线程模型之间,很大的一个区别就是,Scala Actor天然支持线程之间的精确通信。即一个actor可以给其他actor直接发送消息。这个功能是非常强大和方便的。要给一个actor发送消息,需要使用"actor ! 消息"的语法。在Scala中,通常建议使用样例举,即case class来作为消息进行发送。然后在actor接受消息以后,可以使用scala强大的模式匹配功能来进行不同消息的处理。
case class Login(name : String,passsword : String) { }
case class register(name : String,password : String) { }
class UserManageActor extends Actor{ def act(): Unit = {
while (true){
receive{
case Login(name,passsword) => println("name : " + name + ",password : " + passsword);
case register(name,passsword) => println("name : " + name + ",password : " + passsword);
}
}
}
}
//定义一个Object来进行测试
object Object { def main(args: Array[String]): Unit = {
val userManageActor = new UserManageActor;
userManageActor.start();
//userManageActor ! Register("张三三","123456");
//userManageActor ! Login("张三三","123456");
}
} 7.3:Actor之间互相收发消息:
如果两个Actor之间要互相收发消息,那么Scala的建议是,一个actor向另一个actor发送消息时,同时带上自己的引用。其他actor收到自己的消息时,直接通过发送消息的actor的引用,即可以给它回复消息。 7.4:同步消息和Future:
默认情况下,消息都是异步的。但是如果希望发送的消息时同步的,即对方接受后,一定要给自己返回结果,name可以使用!?的方式发送消息。即val reply = actor !? message;如果要异步发送一个消息,但是在后续要获得消息的返回值,那么可以使用Future。即!!语法。val future = actor !! message。val reply = future();

待续......