Scala编程指南

1.scala简介

2004年,martin ordersky发明,javac的编译器,后来spark,kafka应用广泛,twitter应用推广。它具备面向对象和函数式编程的特点。
官网:www.scala-lang.org,最近版本2.12.5,我们用的是2.10.4

2.环境安装

1) windows
    a) 安装jdk-7u55-windows-x64.exe
    b) 安装scala-2.10.4.msi
    安装完以上两步,不用做任何修改。
    测试:在控制台下c:/>scala
    c) 安装eclipse-java-juno-SR2-win32-x86_64.zip 解压缩即可
    d) 安装eclipse的scala插件update-site.zip
        解压会有两个目录,features和plugins,分别把内容放到eclispe对应的目录下。 
    e) 重启eclipse
        提示"Upgrade of scala...",点yes
        提示框"setup Diagnostic",把Enable JDT weaving...选上
        根据提示重启
2) linux

3.第一个程序

1) 交互式编程
    C:\Users\Administrator>scala
    Welcome to Scala version 2.10.4 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_5
    Type in expressions to have them evaluated.
    Type :help for more information.

    scala> 1+1
    res0: Int = 2

2) 脚本形式:通过创建文件来在eclipse/idea中执行代码

    object Test {
        def main(args: Array[String] ) {
            println("Hello world")
        }
    }
    注1:上面这个是单例对象,这个里面只能存放静态的东西
    注2:自动导入2个包
        java.lang._
        scala._
    注3:语句最后一行的分号不推荐写

4.基础语法

1) 变量和常量

    val(常量)和var(变量) 尽可能的用val
    (1) 变量
        格式:
            var 变量名 [:数据类型] = 值
        例:var b :Int = 1
            var c = 2   //类型自动推断
    (2) 常量
        格式:
            val 常量名 [:数据类型] = 值
        例:
            val b :Int = 1
            val b = 1
            b = 2   //报错,常量不能修改

    (3) 常量可以用lazy修饰(了解)
            lazy val b :Int = 1 //b用到的时候再赋值

2) 数据类型

    (1)数据类型
        序号  数据类型    说明
        1   Byte    8位有符号值,范围从-128至127
        2   Short   16位有符号值,范围从-32768至32767
        3   Int     32位有符号值,范围从-2147483648至2147483647
        4   Long    64位有符号值,范围从-9223372036854775808至9223372036854775807
        5   Float   32位IEEE 754单精度浮点值
        6   Double  64位IEEE 754双精度浮点值
        7   Char    16位无符号Unicode字符。范围从U+0000到U+FFFF
        8   String  一个Char类型序列
        9   Boolean 文字值true或文字值false

        10  Unit    对应于无值,等价于void类型,只有一个对象叫()
        11  Null    只有一个对象叫null
        12  Nothing 在Scala中处于最底层,比如创建数组时不指定类型,就是Noting。抽象概念
        13  Any     任何类型的超类型; 任何对象的类型为Any
        14  AnyRef  任何引用类型的超类型
    (2)层次结构
        Scala.Any
            -AnyVal(值)
                -Int,Double等,Unit。
            -AnyRef(引用)
                -List,Set,Map,Seq,Iterable
                -java.lang.String
                -Null
    (3)重点类型:元组
        格式:(元素1, 元素2, ....)
        访问:变量._N 其中N是元组元素的索引,从1开始
            例:var t = ("a", false, 1)       //t的类型是scala.Tuple3
                var value = t._1    //"a"

                var m,n,(x,y,z) =  ("a", false, 1) 
                m   :("a", false, 1) 
                n   :("a", false, 1)

                x   : 'a'
                y   : false:
                z   : 1

    (4)重点类型:字符串
        i) 用的是java.lang.String,但是有时候根据需要,会隐式转换到其它类型,比如调用reverse/sorted/sortWith/drop/slice等方法,这些方法定义在IndexedSeqOptimized中
        ii)多行字符串表示,开始和结束用
    (4)重点类型:字符串
        i)用的是java.lang.String,但是有时候根据需要,会隐式转换到其它类型,比如调用reverse/sorted/sortWith/drop/slice等方法,这些方法定义在IndexedSeqOptimized中
        ii)多行字符串表示,开始和结束用
    (5)了解:符号类型
        符号字面量: '标识符,是scala.Symbol的实例,像模式匹配,类型判断会比较常用。
        var flag = 'start
        if (flag == 'start) println(1) else println(2)

3) 运算符:scala没有运算符,它运算符全部封装成了方法。

    算术:+ - * / %
    比较: == != > < >= <= 
    逻辑:&& || !
    赋值:= += -= *= /* %=
    位:& | ~ ^ >> << >>>  #*/

    注:上面都是方法。例 1+2相当于1.+(2),其中+是方法,2是参数

4) 控制语句

    (1) if,if...else...,if...else if...else...
    (2) scala中的if可以作为表达式用
        var x = if("hello"=="hell") 1 else 0
    (3) switch被模式匹配替换

5) 循环语句

    (1)while,do..while和for都有,while和do..while很像,但是for差别很大。
    (2)for循环格式不同
        for(变量 <- 集合 if 条件判断1;if 条件判断2...) {
            所有条件判断都满足才执行
        }

    (3)没有break和continue,用两种方法可以代替
        i) 用for循环的条件判断
        ii)方法2:非写break,要做以下两步
         //1) 引入一个包scala.util.control.Breaks._
         //2) 代码块用breakable修饰
        break
            breakable {
                for(i<-0 to 10) {
                    println(i)
                    if (i == 5) {break;}
                }
            }
            //0 1 2 3 4 5
        continue
            for(i<-0 to 10) {
                breakable {
                    if (i == 3 || i==6) {
                        break;
                    }
                    println(i)
                }
            }
            //0 1 2 4 5 7 8 9 10

    (5) 集合框架(Array,List,Set,Map)
    在scala中,数组Array归到集合的范畴

    包scala.collection,下面有两个分支immutable(不可改变的,默认)和mutable(可变的)
    (1) 层次结构
     Traversable
        -Iterable
            (immutable不可改变的长度,默认)
            -Set 无序的集合,没有重复元素
                HashSet,TreeSt  其中HashSet内部定义了别名Set
            -Map  键值对,映射
                HashMap,TreeMap  其中HashMap内部定义了别名Map
            -Seq  序列。有先后顺序的元素集合
                -IndexedSeq
                    Array:有索引的元素序列,因此可以通过下标访问
                    Vector,String,Range
                -LinearSeq
                    List:线性的元素序列。访问第1个元素,head方法,最后1个tail方法
                    Queue,Stack
            (mutable可变的长度)
            -Set
                HashSet  其中HashSet内部定义了别名Set
            -Map
                HashMap  其中HashMap内部定义了别名Map
            -Seq
                -IndexedSeq
                    ArraySeq,StringBuilder
                -Buffer
                    ArrayBuffer,ListBuffer
                -LinearSeq
                    LinkedList,Queue
                Stack
    重点记忆几个
        不可变 可变
        Array   ArrayBuffer
        List    ListBuffer
        Map     Map
        Set     Set

    (2) 数组(非常重要):数据类型相同的元素,按照一定顺序排序的集合。
        不可变数组 scala.Array
        可变长数组 scala.collection.mutable.ArrayBuffer
        Array和ArrayBuffer
        1.Array创建
            var 变量名 = new Array[类型](长度) //var arr = new Array[Int](10)
            var 变量名 = Array(元素1,元素2,...) //var arr = Array(1,3,5,7),实际上是调用Array的方法apply

            取值:变量名(下标)
        2.ArrayBuffer创建
            var 变量名 = new ArrayBuffer[类型]() //var arr = new ArrayBuffer[Int]()
            var 变量名 = ArrayBuffer(元素1,元素2,...) //var arr = ArrayBuffer(1,3,5,7)
        3.共同方法
            sum 求和
            max 最大值
            min 最小值
            mkString(分隔符)   例:Array(1,3,5,7).mkString("|")  //结果是1|3|5|7|9
            sorted 排序(从小到大)
            sortBy
            sortWith 自定义排序
            reverse 翻转
            toArray ArrayBuffer转成Array
            toBuffer Array转成ArrayBuffer
            toMap:如果Array内部元素是对偶元组,可以转成map
            toList:转成list
        4.ArrayBuffer的独有方法
            += 追加元素
            ++= 追加集合
            trimEnd(n):删除末尾n个元素
            insert(index,value1,value2,..valueN):第index的位置,插入value1,value2,...valueN)
            remove(index,n):第index个位置删除n个元素
            clear():清空
        5.遍历
            i) 通过下标
                for (i <- 0 to 数组.length-1) {
                    println(i)) //i表示数组的下标
                }
            ii)直接取值
                for (e <- 数组) {
                    println(e)
                }
        6.使用yield创建新数组
            var arr = for (i <- 数组) yield i * 2
        7.多维数组
            var arr = Array(Array(元素1...),Array(元素,...)..)
            遍历
            for (i <- arr) {
                for (j <- i) {
                    println(j)
                }
            }               
    (3) List(不可变)/ListBuffer(可变):List 和java.util.List不同,一旦创建不可以改变。一般做数据分析用List
        1.List定义
            //创建
            var list1 = List("aa","bb","ccc")   //调用apply方法
            var list2 = List.apply("aa","bb","ccc"))
            //右操作符:当方法名以:结尾时,为右操作符,先做右边
            //::右操作符,拼接元素,

            var list3 = "aa"::"bb"::"cc"::Nil   //Nil是空集合,先做"cc"::Nil,其中::是Nil的方法
            var list4 = list1:::"dd"::Nil   //:::拼接集合

        2.常用方法
        //查
            head() 第一个元素
            take(n) 取前n个元素
            tail() 除了第1个的全部元素
            init() 除了最后一个的全部元素
        //增
            :+和+:  前者是尾部追加元素,后者是在头部追加元素,和::很像
            +=或append 追加元素(ListBuffer)
            ++= 追加数组/列表(ListBuffer)
            :: 追加元素(List)
            ::: 追加数组/列表(List)
        //删
            drop(n) 丢弃前n个元素
        //改         
            updated(index,value):用value地替换第index的位置的元素
        //其他
            isEmpty是否为空
            reverse 翻转
            splitAt(m):把列表前m个做成一组,后面是一组
            flatten:以参数的列表作为参数,把所有的元素连接到一起
            //toArray 转成Array
            toArray 转成Array
            zip:两个List内部的元素合并,返回结果是List,元素是对偶元组
            grouped(n):每n个元素分成1组,返回是迭代器Iterator,可以再用toList转成list类型


        3.ListBuffer
            import scala.collection.mutable.ListBuffer
            var list5 = ListBuffer("111","222","333")
    (4) Set:不存在重复元素的集合
            如果用可变的,需要引入import scala.collection.mutable.Set
            //定义
            var s = Set(3.0, 1.0)
            //追加元素
            s += 2.0    //s = s + 2.0
            s += 2.0    //元素重复,加不进去
    (5) Map
        //定义
        var m1 = Map(("zhang3",20),("li4",23),("wang5",21)) //使用元组
        var m2 = Map(("zhang3"->20),("li4"->23),("wang5"->21))
        var m3 = Map[String,Int]()  //定义空map,得用可变的才有意义。
        //添加和更新 
            var m = scala.collection.mutable.Map(("zhang3", 20), ("li4", 23), ("wang5", 21))
            m += (("zhao6", 22)) //
            m = m.updated("zheng7",19) //和上面方法一样
            m.put("wu", 22)     
            m ++= Map(("a" -> 1), ("b" -> 2))
        //删除
            m -= (("zhao6", 22))
            m.clear() 清空
        //查询
            m(key):通过key获取value
            m.get(key) :通过key获取value,返回类型Option
            m.getOrElse(key,获取不到设置的值)
        //其它
            size:元素个数
            contains(key):是否包含key,返回布尔类型
            keys 返回迭代器
            keySet 返回所有的key组成的Set集合
            values 返回迭代器,所有value组成的集合
            mapValues(函数)
            foreach(函数)
        //遍历
    (6) Array和List都是不可变元素,有什么区别?

5.函数

Scala中函数和方法,在类中定义的函数就是方法。

1) 函数声明

    def 函数名([参数列表]):[返回类型] = {
        函数体
        return [表达式]
    }
    注1:如果函数没有返回值,可以返回Unit,也可以不写
    注2:方法的返回类型可以不写,系统自动推导,但是递归必须写。
    注3:函数体的大括号只有一行,可以不写
    例:
        def add(a:Int,b:Int):Int = {
            var sum : Int = 0
            sum = a + b
            return sum
        }

2) 函数调用

    格式:
        方法之间调用:函数名([实参列表])  例:add(3,5)
        对象之间调用:实例名.函数名([实参列表])  例:s.add(3,5)

3) 值函数

    (1)定义
        var 变量名:[输入类型=>输出类型] = {
            (参数列表)=>{函数体}
        }
        其中=>是函数映射符
        在参数列表后不能写返回值类型
        最后一句不能写return

        Lambda表达式(匿名函数,函数字面量):形如(参数列表)=>{函数体}就叫Lambda表达式

4) 高阶函数

    1) 
        定义:本身是函数,参数也是函数或返回值是函数
        例1:
            def m(f:(Double)=>Double):Double = {f(100)}
            def sqrt(x:Double)=Math.sqrt(x)
            调用m(sqrt) 结果是10.0
    2) 常见高阶函数
        集合中常见的高阶函数
        (1)map  
            def map[B, That](f: A => B): That 
            把函数应用于集合中的每一个元素,把返回由返回值组成的一个集合。
            例:
                Array("spark","hive","haddop").map((x:String)=>x.length)
                Array("spark","hive","haddop").map(x=>x.length)
                Array("spark","hive","haddop").map(_.length) // _占位符,代表一个元素

                Array("spark","hive","haddop").map(x=>println(x))
                Array("spark","hive","haddop").map(println _)
                Array("spark","hive","haddop").map(println) //如果只有一个参数,可以不写
        (2) flatMap
            把函数应用于集合中的每一个元素,先做map,得到的结果flatten,生成新的集合。
            例:
                var list = List("a,b,c","d,e,f")
                var list2 = list.map(x=>x.split(",").toList) //List(List(a, b, c), List(d, e, f))
                list2.flatten  //List(a,b,c,d,e,f)

                上面两步可以写成一步,相当于map+flattern
                list.flatMap(x=>x.split(",").toList) //简写:list.flatMap(_.split(",").toList)

                问:map和flatMap有什么区别?
        (3)filter
            参数的函数表示对集合的每个元素过滤,参数函数结果是boolean,最后的返回值还是List
                List(1,2,3,4).filter(x=>x>2)    //List(3,4)
                Array(1,2,3,4,5,6,7,8).filter(x=>x%2==0) //取偶数 2,4,6,8
                Range(1,100).filter(_%2==0) //1到100的偶数
                Array(1,2,3,4,5,6,7,8).filter(x=>x%2==0).map(_*10)//20,40,60,80

                注:filterNot和filter相反
        (4)reduce/reduceLeft/reduceRight
            返回1个值。参数函数要求是二元运算符,作用于集合上,每次返回的结果作为新的输入。
            Array(1,3,5,7).reduce((x,y)=>{println(x + ":" +y);x+y})
            Array(1,3,5,7).reduce(_+_)  //第1个_代表第1个参数,第2个_代表第2个参数

            Array(1,3,5,7).reduceLeft((x,y)=>{println(x + ":" +y);x+y})
            Array(1,3,5,7).reduceRight((x,y)=>{println(x + ":" +y);x+y})
            注:reduce没有特别的顺序,reduceLeft从左到右计算,reduceRigth从右到左计算
        (5)fold/foldLeft/foldRight
            和reduce很像,只是增加了一个初始值,但是做并行时要注意。
            Array(1,3,5,7).fold(4)(_+_)     //20
            List(2.0,3.0).fold(4.0)(Math.pow)  //4096.0
        (6) par
            把计算转换为并行化,把reduce拆分多个任务
            Array(1,3,5,7,8).par.fold(4)(_+_)
            Range(1,10).par.map(println)
        (7) groupBy 
            对列表进行分组,返回结果是map
            var data = List(("zhang3","male"),("li4","female"),("wang5","male"))
            data.groupBy(x=>x._2) //Map(male-> List((zhang3,male), (wang5,male)), female -> List((li4,female)))

            多介绍一个grouped(n):每n个分成一组
            例:List(1,3,5,7,9).grouped(2).toList //List(List(1, 3), List(5, 7), List(9))
        (8)partition
            把列表分成两部分,第一个为满足条件的,第二部为不满足条件的
            List(1,3,5,7,9).partition(x=>x>4) //分成两组:(List(5, 7, 9),List(1, 3))
        (9)diff差集,union并集并保留重复,intersect交集
            var n1 = List(1,2,3)
            var n2 = List(2,3,4)
            var n3 = n1.diff(n2) //也可以写n1 diff n2 ,结果:List(1)
            var n4 = n2.diff(n1) //List(4)
            var n5 = n1 union n2    //List(1 2 3 2 3 4)
            var n6 = n1 intersect n2    //List(2,3)
        (10)distinct
            去掉重复元素
            List(1,2,3,2,3,4).distinct // List(1, 2, 3, 4)
        (11)mapValues(函数)
            还是map功能,只是处理的值是value,最终结果是map
            Map(("zhang3",20),("li4",23),("wang5",21)).mapValues(_*2)//结果Map(zhang3 -> 40,li4 -> 46,wang5 -> 42)
        (12)foreach(函数)
            和map一样,区别是它没有返回值,通常用在遍历
        (13)sorted 排序和sortBy和sortWith
            sorted 排序,最简单,从小到大
                List(2,1,3,5,6,4).sorted    //1 2 3 4 5 6
            sortBy:按照第几列排
                List(("a",3),("b",1),("c",2)).sortBy(_._2) //按照第2列排
            sortWith:自定义排序
                List(2,1,3,5,6,4).sortWith((x,y)=>x>y) //从大到小

    3)下划线_的用法
        (1) 让匿名函数更简洁,可以用下划线_当做一个占位符。
        (2) java中导入包下的所有类用*,在scala中用_
            例:
                Java:  import java.util.*
                scala: import java.util._
        (3) 下划线_可以作为部分应用函数
    4) 函数柯里化
        柯里化(curring) 把接收多个参数的函数编程接收一个单一参数
        例1:不是柯里化,但是和柯里化很像
        def foo(factor : Int) = (x:Double)=>factor * x;
        调用:
        var f = foo(10)
        f(50)
        也可以直接写:foo(10)(50)
        例2:柯里化
        def foo(factor : Int)(x:Double) = factor * x
        调用:foo(10)(50)
        注:柯里化函数不是高阶函数,不能这么调用var f = foo(10)

    5)部分应用函数(Partial Applied Function)
    部分应用函数, 是指一个函数有N个参数, 而我们为其提供少于N个参数, 那就得到了一个部分应用函数.,柯里化函数和普通函数都有部分应用函数

    6)偏函数(Partial Function)
        (1)简介
        引子:
        scala> List(1,2,3) map {case i:Int=>i+1}
        scala> List(1,2,3) map {(i:Int)=>i+1}

        上面两个结果是一样的。

        偏函数:上面例子中map后面的被包在花括号内的代码,也就是没有 match的一组case语句,就叫偏函数。

        偏函数类型是:PartialFunction[A,B],A的参数类型,B是返回结果类型。平常我们都是直接用。

        例1:
        val foo : PartialFunction[Int,String] = {   //类型PartialFunction[Int,String]可以省略
        case y if y % 2 == 0 => y + " 是偶数"
        }
        println(foo(10)) //10 is Even
        println(foo(11)) //抛出错误: scala.MatchError

        ----反编译:相当于foo是一个对象,调用对象的 apply方法
        private final PartialFunction foo = new PartialFunction{
        public final Object apply(int i){
            Object obj;
            if (i % 2 == 0)
                obj = y + " 是偶数"
            else
                obj = i;
            return obj;
        }
        }
        foo.apply(10)   

        (2)orElse:可以把多个偏函数结合起来,效果类似case语句。
        val or1 = {case 1 => "One"}
        val or2 = {case 2 => "Two"}
        val or_ = {case _ => "Other"}
        val or = or1 orElse or2 orElse or_ //使用orElse将多个偏结合起来
        //测试:
        scala> or(1)    // one        
        scala> or(20)   //other

        (3) andThen: 相当于方法的连续调用,比如g(f(x))。  
        val a = {case cs if cs == 1 => "One"}
        val b = {case cs if cs eq "One" => "The num is 1"}
        val c = a andThen b
        //测试
        num(1)  //The num is 1
        (4) 普通函数的偏应用函数定义
            比如我先定义一个函数:
                def sum(a:Int,b:Int,c:Int) = a + b + c;
            那么就可以从这个函数衍生出一个偏应用函数是这样的:
                def foo = sum(1, _:Int, _:Int)          
            调用foo(2,3), 相当于调用sum(1,2,3) 两个_分别对应函数sum对应位置的参数.        
            用处:当你在代码中需要多次调用一个函数, 而其中的某个参数又总是一样的时候, 使用这个可以少敲一些代码

        (5)柯里化函数中的偏应用函数定义
            def sum(a:Int)(b:Int)(c:Int) = a + b + c; // (a: Int)(b: Int)(c: Int)Int
            val result = sum _ //柯里化赋值,结果是部分应用函数 Int => (Int => (Int => Int)) = <function1>
            sum(1)(2)(3)    //6
            result(1)(2)(3) //6
            result(1)(_: Int)(3) //部分应用函数:Int => Int = <function1>
            2018/4/5

6.面向对象

1) 类和对象

    (1) 简介
        scala中的类和java中的类一样,都是通过class定义
        class Person { //不写public,默认public
        //var name:String;  //报错,变量必须要被初始化,而java不需要被初始化,系统会自动赋初值
        var name :String = null //正确
        var age :Int = _    //正确,不知道赋上面值时,用_占位符
        }
    (2)创建对象
        var p1 = new Person()
        var p2 = new Person //没有参数,可以省略()
    (3) 类成员访问
        p1.name = "zhang3"  //设置,底层调用的是p1.name_=("zhang3"),其中_=是方法
        println(p1.name)    //取值
        非要用getter和setter,属性前加入 @BeanProperty(导入包:scala.beans._)
    (4) 单例对象
        scala中不支持静态成员的语法,通过单例对象实现
        object Test {
            private var id:Int = 0
            def m() ={
                id += 1
                id
            }
        }
        测试:
        scala>Test.m    //1
        scala>Test.m    //2
    (5) 伴生对象和伴生类
        单例对象和类如果同名的话,单例对象也叫伴生对象。
        例:
            class A {}
            object A {
            }

        伴生对象和伴生类可以互相访问,即使是private也可以访问,但是private[this]不能访问

        一个文件可以有多个类
    (6) apply方法
    object Dog {
        def apply():Unit = {
            println(1)
        }
        def apply(name:String):Unit = {
            println(2)
        }
        def main(args: Array[String]) {             
            var d1 = Dog()  //调用Apply()方法
            var d2 = Dog("hello") //调用Apply(String)方法
        }
    }

2) 主构造方法

    (1)定义
        每个类都有主构造方法,参数放在类名后,构造方法和类交织在一起。
        class Student(var name:String, var age:Int) 
        反编译代码:
            public class Student {
                private String name;
                private int age;
                public Student(String name, int age) {
                    println("hello:" + name)
                    this.name = name;
                    this.age = age;
                }
                //getter和setter
                //name()和name_$eq(String)
                //age()和age_$eq(int)

                @Override
                public String toString() {
                    return name + ":" + age
                }
            } 
            Student s = new Student();
            System.out.println(s) //打印s,就是打印s.toString()
        例:
            class Student(var name:String, var age:Int) {
                println("hello")    //加在主构造方法中
                override def toString = name + ":" +age
            }
            var s = new Student()
            println(s)
     (2) 带有默认参数的主构造方法
        构造方法可以带有默认参数。
        class Student(var name:String, var age:Int, val gender:String="female") {
            println("执行主构造方法")
        }
        var s1 = new Student("zhang3", 18, "male") //正确
        var s2 = new Student("li4", 18) //正确
        s2.name = "wang5"
        s2.gender = "male"
    (3) 私有构造方法
        class Student private(var name:String, var age:Int)
        var s = new Student("li4", 18)//报错

3) 辅助构造方法

    (1) 非主构造方法,方法名用this
    (2) 非主构造方法,也可以有默认值
    class Dog(val id:String) {
        var name :String = "大黄"
        def this(id:String,name:String) {
            this(id) 
            this.name = name
        }
        override def toString = name + ":" + id
    }
    //测试
    var d1 = new Dog("1111")
    var d2 = new Dog("2222","二黄")
    println(d1)
    println(d2)

4) 继承和多态

    (1) 继承用extends
         class Person(var name:String, var age:Int)
         //没有写var,表示继承父类的属性
         class Student(name:String,age:Int, var no:String) extends Person(name,age)
         //测试
         new Person("tom", 18)
         new Student("jack",19,"123")
        Student从父类继承name和age,所以前面不要加变量var或者val,no是新加的元素,所以需要var
    (2) 多态
        多种形态,动态绑定,在执行期间确定引用对象的实际类型,根据其实际类型调用相应的方法,也就是
        子类可以赋值给父类。
    ·   class Human(var name:String, var age:Int) {
            def walk():Unit = {
                println("walk in Human")
            } 
        }
        class Teacher(name:String,age:Int) extends Human(name,age) {
            override def walk():Unit = {
                println("walk in Teacher")
            } 
        }
        class Doctor(name:String,age:Int) extends Human(name,age) {
            override def walk():Unit = {
                println("walk in Doctor")
            } 
        }
        //测试
        var x:Human = new Teacher("tom", 18)
        var y:Human = new Doctor("jack", 19)
        x.walk  //walk in Teacher
        y.walk //walk in Doctor

5) 成员访问控制

    private 类内+伴生对象
    protected 同包+子类
    不写:默认是public,都可以访问

    特别之处
    (1)  private[this] 只能类内访问
        class Person {
            private[this] var name:String = "zhang3"    //只能类内
            private var age: Int = 18   //类内+伴生对象
        }
        object Person {
            def main(args:Array[String]) {
                var p = new Person
                print(p.age) //18
                //print(p.name) //报错
            }
        }
    (2) 主构造方法也可以加入成员访问控制
        class Person(private var name:String, protected var age:Int)

6) 抽象类

    不能能被实例化的类。可以有抽象方法,非抽象方法,成员变量。但是scala增加了抽象成员变量。
    abstract class Person {
        var name:String //抽象成员变量
        def walk        //抽象方法
    }
    //子类如果继承,必须要实现抽象变量和抽象方法,如果不想实现,子类也需要是抽象类
    class Student extends Person {
        override var name:String = _            //覆盖父类的抽象变量
        override def walk():Unit = {println("walk in Student")} //覆盖父亲的抽象方法
    }

7) 内部类和内部对象

    定义在对象或者类内部的类
    object Ex extends App {
        class Student(var name:String, var age:Int) { 
            //内部类
            class Grade(var name:String)
            //内部对象
            object Utils {
                def print(name:String) = println(name)
            }
        }
        object Student {
            //内部类
            class Printer {
                def print(name:String) = println(name)
            }
        }
        var s = new Student("tom", 18)
        //var grade = new Student.Grade("xxx")  //此处出错,怎么纠正?
        s.Utils.print("student:util.print(String)")
        new Student.Printer().print("Student.Printer().print(String)")
    }

8) 匿名类

    没有名字的类,一般用在某个类只在代码出现一次
    abstract class Person(var name:String, var age:Int) {
        def print():Unit
    }
    //调用
    //var p = new Person("tom", 18) //错误,抽象类不能new,也就是不能创建实例
    var p = new Person("tom", 18) {
        def print():Unit = println("hello")
    }
    p.print();

9) trait(特质),其实就是接口,相当于jdk1.8的接口,比jdk1.8以下版本的接口多了非抽象的变量和方法

        (1) scala没有提供接口interface,而用trait关键字,其实和接口等价。
        使用方法:extends或with,第一个实现的特质用extends,之后的用with
        它和抽象基本一样,可有抽象成员,抽象方法,具体成员和具体方法
        例:
            trait Closeable {
                def close():Unit
            }
            trait Clonable {
                def open():Unit
            }
            //第一个接口用extends,后面的用with
            class File(var name:String) extends Closeable with Clonable{
                def close():Unit = println("file close")
                def open():Unit = println("file open")
            }
            //写一个全的trait
            trait Book {
                var a : Long = _    //具体成员
                var b : Long        //抽象成员
                def add(x:Int)      //抽象方法
                def add2(x:Int):Int = x+2   //具体方法
            }
            var b = new Book(){ //定义匿名内部类,只需要实现抽象方法和抽象成员变量即可
                override var b : Long = 1
                def add(x:Int) = println(x)
            }
            b.add(6)
            println(b.add2(5))

10) 自身类型(self type)

    trait A {
        val a = 1
    }
    trait B {
        this:A=>        //引入接口A,相当于把A的东西全放进来
        var b = 2
        def xx() {
            println(this.a + ":" + this.b)
        }
    }
    class C extends B with A
    //调用
    new C().xx()
    //还有一种特殊用法,this定义别名
    class D {
        self=>  //为this定义别名self,也可以是其他名字
        var x = 2
        def foo = self.x + this.x
    }
    //常见用法,用在内部类引用外部类的变量
    class OuterClass {
        xx=>    //为this定义别名
        var value = "here"
        class InnerClass {
            println(xx.value)
        }
    }

7.包

1) 包定义

    (1) 第一种定义方式,和java很像,
        package cn.cda.day1
    (2) 第二种定义方式
        package cn{
            package cda {
                package day1 {
                    class Teacher {

                    }
                }
                package day2 {
                    class Student
                }
            }
        }
        以上方式不推荐

2) 作用域

    scala中有private,proteced和默认(public)外,还有private[x]和protected[x],其中x可以是包/类/单例对象

3) 包对象

    通过package关键字声明的对象为保对象,主要用于对常量和工具函数的封装,通过包名引用。
    package object Math {
        val PI = 3.14
    }
    class Test {
        def main(args:Array[String]) {
            m(3)    //3.14*9=28.62
        }
        def m(r:Double) = Math.PI * r * r
    }

4) import

    导入包下的所有的类和对象,还可以引入重命名和类隐藏等功能。
    1) 隐式导入
        默认导入的包,通过下面命令查看
        scala>:import
    2) 引入重命名
        import java.util.{HashMap=>JavaHashMap}
        import scala.collection.immutable.HashMap

        var map = new JavaHashMap
    3) 类隐藏
        引入java.util.下的所有类,但是不想要HashMap
        import java.util.{HashMap=>_,_} //隐藏HashMap
        import scala.collection.immutable.HashMap
        var map = new HashMap

8 模式匹配

1) 简介

    模式匹配类似switch语言,简单用法就是替代多层的if else语句和类型检查和快速匹配。
    它使用了关键字match来替换switch,一旦匹配到了,后面不再执行,所以不用break语句。
    scala有一个十分强大的模式匹配机制,可以用到很多场合。如switch,类型检查等,

    格式:
        case 变量: 类型=> 代码

    例:java和scala进行比较 
    --java
        for (int i = 1; i < 5 ; i++) {
            switch (i) {
                case 1:System.out.println(1);break;
                case 2:System.out.println(2);//不带break会陷入其它分支,同时输出2,3
                case 3:System.out.println(3);break;
                default:System.out.println("default");
                //case语句不能接表达式
                //case i%5==0:System.out.println("10")
            }
        }

    --scala
        for (i <- 1 to 5) {
            i match {
                case 1 => println(1)        //不需要写break,
                case 2 => println(2)
                case 3 => println(3)                
                case _ => println("default")
            }
        }
        //上面的case语句中,还可以加入表达式
        for (i <- 1 to 5) {
            i match {
                case 1 => println(1)        //不需要写break,
                case x if (x%2 == 0) => println(s"$x 是偶数")     //加入表达式 ,此时的x是形参,i是实参   
                case _ =>   //这句话表示其它任何情况都不进行操作
            }
        }
        //还可以作为函数体
        def m(x:Int) = x match {
            case 5 => "整数5"
            case y if (y % 2 == 0) => "能被2整除"  //注意y认为是形参
            case _ => "其它整数"
        }
        println(m(5)
        //反编译:
         public String m(int x){
            switch (x){
             default: tmpTernaryOp = "其它整数"; break;
            }
            return  x % 2 == 0 ? "能被2整除" : "整数5";"
          }  

2) 模式匹配的7大类型

    (1)常量模式
        case后面接的全部是常量
        例:for (i <- 1 to 5) {
            i match {
                case 1 => println(1)        //不需要写break,
                case 2 => println(2)
                case 3 => println(3)                
                case _ => println("default")//可以写成 case x
            }
        }
    (2)变量模式
        case后面接的是变量
        例:
            def m(i:Int) = i match {
                case x if (x % 2 == 0) => "能被2整除"       //这个顺序是有关系的
                case x if (x % 3 == 0) => "能被3整除"   
                case x => "能被2或3意外的数"   // 等价于 case _
            }
            //调用
            println(m(5))
            println(m(4))
            println(m(3))   


    (3)构造方法模式
            与创建对象相反,见下
            例1
            case class Dog(val name:String, val age:Int)
            def m(x:AnyRef) = x match {
                case Dog(name,age)=>println(name+":"+age)// 构造函数模式,与创建对象相反,对对象进行析构,调用unapply方法,普通类没有,case类系统会生成。
                case _=>
            }
            //测试
            val dog = Dog("Pet", 2) //调用apply创建对象
            m(dog)
            例2:对部分变量析构
            case class Dog(val name:String, val age:Int)
            def m(x:AnyRef) = x match {
                case Dog(_,age)=>println(age) //表示匹配这个成员域,但是不需要使用nam的值
                case _=>
            }
    (4)序列模式
        用于匹配Seq集合的,用法和上面类似,但是可以使用_*来匹配多个元素,并且只能放在模式内的最后
        例:
            def m(x:AnyRef) = x match {
                case Array(a,b)=>println("1")
                case Array(a,_,b,_*)=>println("2")  //_匹配第2个元素,_*匹配任意多,只能放模式最后
                case _=>
            }
            //调用 
            m(Array(1,2,3,4))

    (5)元组模式
        和上面用法相似,但是不能使用_*,_*只能在序列模式中使用。
        例:
            def m(x:AnyRef) = x match {
                case (a,b)=>println("1")
                case (a,_,b,_)=>println("2")
                //case (a,_*)//元素模式不能使用_*
                case _=>
            }
            var t = (1,2,3,4)
            m(t)
    (6)类型模式
        匹配变量的类型
            class A
            class B
            class C extends A
            def m(x:AnyRef) = x match {
                case y:String=>println(1)
                case y:B=>println(2)
                case y:A=>println(3)
                case _=>println(4)
            }
            //测试
            val b = new B
            val c = new C
            m("hello")
            m(b)
            m(c)
        反编译源代码  
         public void m(Object x)
          {
            if ((x instanceof String)){
              this.println(1);
            }
            else if ((x instanceof B)){
              this.println(2);
            }
            else if ((x instanceof A)){
              this.println(3);
            }
            else  {
             this.println(4);
            }
          }
    (7)变量绑定模式
        如果想要的不是析构对象,而且返回匹配模式的对象,很常用

        例1
        case class Dog(val name:String,val age:Int){
            override def toString=name+":"+age
        }
        def m(x:AnyRef) = x match { 
            //Dog(_,_)用于匹配输入对象中包括两个成员变量的Dog对象,匹配成功后把对象赋值给d
            case d@Dog(_,_) => println("变量绑定模式返回的变量值:" + d)
            case _=>
        }
        // 测试
        val dog = new Dog("Pet", 2)
        m(dog)
        反编译源代码:
         public void m(Object x) {
            if ((x instanceof Dog)){
              Dog d = (Dog)x;
              this.println("变量绑定模式返回的变量值:" + d)
            }
            else   {
            }
          }



        例2:更常见,也更复杂
        val list = List(List(1,2,3,5),List(4,5,6,7,8,9))
        def m(x:AnyRef) = x match {
            //变量绑定模式
            case e1@List(_,e2@List(4,_*))=>println("e1:" + e1 + ",e2:" + e2)
            case _=>
        }
        //调用 
        m(list)//e1:List(List(1, 2, 3, 5), List(4, 5, 6, 7, 8, 9)),e2:List(4, 5, 6, 7, 8, 9)
        ---反编译代码:
        public void m(Object x)  {              
                if ((x instanceof List))
                {
                  List e1 = (List)x;
                  Some localSome1 = List..MODULE$.unapplySeq(e1);
                  if ((!localSome1.isEmpty()) && (localSome1.get() != null) && (((LinearSeqOptimized)localSome1.get()).lengthCompare(2) == 0))
                  {
                Object e2 = ((LinearSeqOptimized)localSome1.get()).apply(1);
                if ((e2 instanceof List))
                {
                  List localList2 = (List)e2;Some localSome2 = List..MODULE$.unapplySeq(localList2);
                  if ((!localSome2.isEmpty()) && (localSome2.get() != null) && (((LinearSeqOptimized)localSome2.get()).lengthCompare(1) >= 0))
                  {
                    Object localObject2 = ((LinearSeqOptimized)localSome2.get()).apply(0);
                    if (BoxesRunTime.equals(BoxesRunTime.boxToInteger(4), localObject2))
                    {
                      Predef..MODULE$.println(new StringBuilder().append("e1:").append(e1).append(",e2:").append(localList2).toString());localBoxedUnit = BoxedUnit.UNIT; return;
                    }
                  }
                }
                  }
                }
              }

3) 正则表达式与模式匹配(略)

    (1)Scala正则表达式       
    (2)正则表达式在模式匹配中的应用

4) for循环种的模式匹配

    (1)变量模式匹配
        for((x,y)<-Map("a"->1,"b"->2,"c"->3)) {
            println(x + ":" + y)
        }
    (2) 常量模式匹配
        //下面只有"b"被匹配到,输出2
        for(("b",y)<-Map("a"->1,"b"->2,"c"->3)) {
            println(y)
        }
    (3) 变量绑定模式匹配
        //可以绑定变量xxx和yyy
        for((xxx@"b",yyy@y)<-Map("a"->1,"b"->2,"c"->3)) {
            println(xxx + ":" + yyy)
        }
    (4) 类型模式匹配
        //类型value为字符串才能匹配到
        for((x,y:String)<-Map("a"->1,"b"->"hello","c"->3)) {
            println(x + ":" + y)
        }
    (5) 构造方法模式匹配
        case class Dog(val name:String, val age:Int)
        case class Cat(val name:String, val age:Int)
        for (Dog(name,age) <- List(Dog("a",1),Cat("b",2),Dog("c",3))) {
            println(name + ":" + age)
        }
        上面输出第1个和第3个
        for (List(first,_*) <- List(List(1,2,3), List(4,5,6,7))) {
            println(first)
        }
    (6) 序列模式匹配
        可以用_*

5) 样例类和样例对象

    样列类(case class):使用case修饰的类。就是增加了一些方法,比如unapply。用在上面的模式匹配.   

    1) 样例类:是一种特殊的类,可以用于模式匹配。case class是多例,case object是单例。

        上面已经做了一个例子了,下面再做一个例子,用到了sealted.
        trait中前面加入 seatled,表示密封类
            a) 其修饰的trait,class只能在当前文件里面被继承
            b) 并且用sealed修饰这样做的目的是告诉scala编译器在检查模式匹配的时候,让scala知道这些case的所有情况,scala就能够在编译的时候进行检查,看你写的代码是否有没有漏掉什么没case到,减少编程的错误。
        例:
        sealed trait A
        case class B(id:String,name:String) extends A
        case class C(id:String) extends A
        case class D(id:String) extends A

        def m(x:A) = x match {
            case B(id,name)=>"1"
            case C(id)=>"2"     // 这一行如果不写,系统会警告,这样的好处是提示你不要漏了
            case D(id)=>"3"
        } 
        //测试
        val b = new B("1","zhang3")
        println(m(b))
        // 注意,如果注释掉上面的B,C,D中的任何一句,系统会警告,所以在子类众多的时候,要用 sealed修饰带来的好处。


    2) 样例对象(case object)
        上面看到每个子类都有自己的属性,但是实际开发,可能不需要属性,如果不定义属性,编译器会警告。
        这时候需要定义样例对象(case object)来声明

        sealed trait A
        case class B(id:String,name:String) extends A
        case class C(id:String) extends A
        case class D(id:String) extends A
        case object E extends A

        def m(x:A) = x match {
            case B(id,name)=>"1"
            case C(id)=>"2"     
            case D(id)=>"3"
            case E=>"4"
        } 
        //测试
        val b = new B("1","zhang3")
        println(m(b))
        println(m(E)) //直接调用

        分析:
            a)case class会先创建对象,而case object可以直接使用
            b)case class会生成两个字节码文件,而case object是一个
            c)case class生成的伴生对象会自动实现 apply和unapply方法,而case object不会
         使用 case object可以提高速度,减少开销 

        例2:spark中的一段代码如下,看学生是否看懂?

        import scala.util.Random

        case class SubmitTask(id: String, name: String)
        case class HeartBeat(time: Long)
        case object CheckTimeOutTask    //定义样例对象

        object Test extends App {
          var arr = Array(CheckTimeOutTask, HeartBeat(2333), SubmitTask("0001", "task-001"));
          arr(Random.nextInt(arr.length)) match {
            case SubmitTask(id, name) => { println(s"$id,$name") }
            case HeartBeat(time) => { println(time) }
            case CheckTimeOutTask => { println("check") }
          }

    3) Option类型
        Option类型也是样本类,表示值是可选的,要么无值,要么有值,Option的子类有Some和None
        用Some(x) 表示有值,x是真正的返回值
        用None表示无值
        val map = Map("cn"->1,"jp"->2)      
        val v = map.get("cn") match {   //get方法返回 Option类型
            case Some(i) => i
            case None => 0
        }
        查看map集合的源代码: def get(key: A): Option[B]
        查看some的源代码:case class Some[+A](x: A) extends Option[A] 
        查看None的源代码:case object None extends Option[Nothing] {

        我们常用 getOrElse("c",0)

9 隐式转换

1)隐式转换简介

    scala提供的一种强大的语法特征,必须掌握。开发中,会自动转换。
    scala定义了一些隐式转换,会自动导入他们。
    比如 1 to 5 调用 1.to(5),但是to并不是Int类型的方法,它会自动转换为 scala.runtime.RichInt

    源代码:系统自动导入Predef对象,在scala.Predef中,objectPredef父类LowPriorityImplicits中方法intWrapper(x:Int)=new runtime.RichInt(x)

    不仅如此, Int还可以转换为其它类型(AnyRef,Integer,Double,Float,Long),可以由其它类型转换而来
    查看所有的隐式转换
    scala>:implicit -v

    scala.Int的伴生对象包含了Int到其它类型的转换
        implicit def int2long(x:Int) : Long = x.toLong
        implicit def int2float(x:Int) : Float = x.toFloat
    Predef对象中也可以了大量的隐式转换函数,如Scala数值类型到java数值类型的转换
        implicit  def byte2Byte(x:Byte) = java.lang.Byte.valueOf(x)
    正是这些隐式转换函数的存在,简化了Scala代码。       

2) 隐式转换

    (1)隐式转换函数
        Scala默认提供了大量的隐式转换,比如Int到Float,但是如果想实现Float到Int,需要自己定义
        例:
            val value : Int = 1.0f  // 报错
            修改:
            implicit def m(x:Float) = x.toInt   //toInt方法有,但是没有隐式转换
            val value : Int = 1.0f
        原理:当编译机发现类型不匹配,就会查找隐私转换函数
        --------xjad反编译-----------  
            public int m(float x){
                return (int)x;
            }
            int value = m(1.0F);
            System.out.println(value);          

        }
    注:名称可以是任意(与参数类型和返回值有关),但不要重复,否则隐式转换报错,不过尽量给名字写的有意义
    (2) 隐式类
        隐式类的转换不像隐式转换函数那么明显。
        例;
            implicit class A (val name:String) { 
                def bark = println(name + " is barking")
            }
            //调用
            "Nacy".bark //由于String没有 bark方法,因此会找隐式类,由于A的主构造方法是String,就会调用。

            ---源代码          
            public static class Test$A{
                private final String name;
                public Test$A(String name) {this.name = name}
                public void bark(){
                    System.out.println(name + " is barking");
                }
            }           
            public final class Test${

                public void main(String args[])
                {
                    new Test.A("Nacy").bark();//把“nacy”转换为创建A的对象
                }
            }
    (3) 隐式对象
        单例对象前加入implicit关键字
        trait A[T] {
            def m(x:T):T    
        }
        //隐式转换对象:整数的平方
        implicit object AInt extends A[Int]{
            def m(x:Int) = x * x
        }
        //隐式转换对象:字符串的乘积
        implicit object BString extends A[String] {
            def m(x:String) = x * 2
        }
        //定了一个函数:函数具有泛型参数
        def foo[T:A](x:T) = {
            val obj = implicitly[A[T]] //通过 implicitly方法,知道T的类型,创建A的相应子类对象
            obj.m(x)
        }
        //调用
        println(foo(5)) //25,从下面源代码可以看到,翻译成java时传递两个参数
        println(foo("5"))//"55"
        ------源代码----
        public final class Test${
            public Object foo(Object x, Test.A o)   { //传递A的子类对象
                Test.A obj = (Test.A)(o);
                return obj.m(x);
            }
            public void main(String args[])         {
                foo(5,传递Test.AInt的单例对象);
            }
        }
        public final class Test{
            interface A{
                Object m(Object obj);
            }
        }
        public static class AInt implements A{
            public int m(int x){    return x * x;   }
        }
        public static class BString implements A{
            public String m(String x){ return x * 2}
        }
    (4) 隐式参数 
        上面的implicitly方法换一种形式,定义在参数中 , 参数前加了implicit,这种形式的参数成为隐式参数
        上面代码可以化简如下:
            def foo[T:A](x:T)(implicit obj:A[T]) = { //知道T的类型,创建A的相应子类对象
                obj.m(x)
            }
        //编译后的源代码基本不变
    (5) 隐式值
        隐式值也叫隐式变量,前面用implicit修饰
        例1:
        implicit val x : Double = 2.55
        def sqrt(implicit y: Double) = Math.sqrt(y)
        //测试 
        scala> sqrt //不指定参数,编译器再当前作用于查找相应的隐式值或隐式对象,找到x传给它
        --xjad反编译

        public final class Test${
            private final double x = 2.5499999999999998D;   

            public double sqrt(double y) {
                return Math.sqrt(y);
            }
            public void main(String args[]){
                sqrt(x);
            }
        }
        例2:
        改写上面的代码:
            trait A[T] {
                def m(x:T):T    
            }
            class AInt extends A[Int]{ //普通类
                def m(x:Int) = x * x
            }
            class BString extends A[String] {
                def m(x:String) = x * 2
            }

            implicit val a = new AInt   //隐式值,上面的例子是隐式对象
            implicit val b = new BString

            def foo[T:A](x:T)(implicit obj:A[T]) = { 
                obj.m(x)
            }
            //调用
            println(foo(5)) //25
            println(foo("5"))//"55"

3) 隐式参数使用常见问题

    1. 当函数没有柯里化,implicit关键字组用于函数列表中的所有参数
        scala>def m(implicit x:Double,y:Double) = x * y     //所有参数都作用implicit
            m: (implicit x: Double, implicit y: Double)Double
        scala>implicit val d = 2.5
        scala>m //结果 6.25
    2. 隐式函数使用时要么全部指定,要么全不指定,不能只指定部分
        如果上面调用
        scala>m(3.0) 就报错
        scala>m(3.0,3.0)
    3. 同类型的隐式值只在当前作用域内出现1次
        如果再定义 
        scala>implicit val d2 = 3.5
        scala>m // 报错 
    4. 在定义隐式函数时,implicit关键字只能在参数开头
        //报错,不能定义在第2个参数
        def m(x:Double,implicit y:Double) = x * y   
    5. 如果想达到函数def m(x:Double,implicit y:Double)只将参数y定义为隐式函数的目的,需要对函数柯里化
        //柯里化后,只有参数y是隐式参数
        def m(x:Double)(implicit y:Double) = x * y
        implicit val d = 4.0
        m(3)
    6. 柯里化的函数implicit关键字只能作用于最后一个参数
        def m(x:Double)(y:Double)(implicit z:Double) = x * y * z //正确
        def m(implicit x:Double)(y:Double)(z:Double) = x * y * z //错误,没有作用于最后一个 
    7. implicit关键字只在隐式参数中出现1次,对柯里化的函数也不例外
        def m(implicit x:Double,implicit y:Double) = x * y // 错误
    8. 匿名函数不能使用隐式参数
        val m = (x:Double, y:Double)=>x * y // 正确
        val m = (implicit x:Double, y:Double)=>x * y
    9. 柯里化的函数如果有隐式参数,则不能使用其偏应用函数
        def m(x:Double)(y:Double) = x * y
        val t1 = m _    //赋值给t1, 定义两个参数的偏函数
        t1(3,0)(4,0)
        val t2 = m(3.0) _
        t2(4.0)
        以上都没有问题,但是如果下面有隐式参数,则会报错 
        def m(x:Double)(implicit y:Double) = x * y
        val p = m _ //报错

4)隐式转换规则与问题

    (1) 隐式转换的若干规则
        a) 作用域规则
            必须在当前作用域才能起作用。
        c) 无歧义规则
            不能存在两个一样功能的隐式转换函数。
        d) 一次转换原则
            只经过1次隐式转换,不能经过多次

    (2) 是否调用隐式转换
        非必要的情况下,尽量少用,如果是为了炫耀,大胆使用。

10 类型参数

1) 简介

    (1) 简介
        在类的声明中,定义一些泛型,然后在类内部,就可以使用这些泛型类型。
        scala泛型和java泛型基本相同,只是Java语法格式为<>,而Scala语法给是为[]

        //比如: java中List<T> ,而Scala中为 List[T]
        //比如:
        trait Map[A,B] extends Iterable[(A,B)] with GenMap[A,B] with scala.collection.Map[A,B] {
                override def empty:Map[A,B] = Map.empty
             }      
    (2) 泛型类
        class Person[T](var name :T)
        class Student[T,S](name:T, var age:S) extends Person(name)//name前不写var表示继承
        例:
            新生入学,填写id时候,有的人用整数,有的人用字符串
            class  Student[T](val id:T) {
                def get(hukouId:T) = id + ":" + hukouId
            }
            val a = new Student[Int](111)
            a.get(4)

            val a = new Student(111) //不写Int,可以自动推断

            val a = new Student[String]("111")

            //测试
            class Student[A,B](var name:String, var age:Int)
            println(new Student[String,Int]("a",1).name)
    (3) 泛型方法
        与泛型类类似,给某个函数在声明时指定泛型类型
        def getCard[T](content: T) = {
            if (content.isInstanceOf[Int]) 1        //等价于java的instanceOf
            else if (content.isInstanceOf[String]) 2
            else 3
        }
        //测试
        getCard(111)        //1
        getCard("222")      //2
        getCard(false)      //3

    (4) 类型通配符
        在java中,采用List<?>来做,但是scala中,没有采用上面的特性,是通过存在类型来解决
        类[T,U,..] forSome {type T;type U;...}
        例:
        //存在类型Array[T] forSome {type T}
        def printAll(x : Array[T] forSome {type T}) = {
            for (i <- x) {println(i)}
        }
        ---源代码:
        public void printAll(Object x)              {
            Predef$.MODULE$.genericArrayOps(x).foreach(new Serializable() {
                public final void apply(Object i)                       {
                    System.out.println(i);
                }
            });
        }

        //可以写简化形式
        def printAll(x : Array[_]) = {
            for (i <- x) {prntln(i)}
        }           

2) 类型变量界定

    对泛型范围进行界定,而不是任意类型。比如必须是某个类的子类,或者是父类,才能正常使用。
    java中,这么定义:
    //List<? extends Object> 接收object的子类,包括Object
    //List<? super Integer> 接收Integer的父类,包括Integer

    scala没有采用,它采用对应
    <:
    >:
    例1:
    class Person(val name: String) {
    def makeFriends(other: Person) = println(this.name + "和" + other.name + "交朋友")
    }
    class Student(name: String) extends Person(name)

    class Party[T <: Person](p1: T, p2: T) {
    def play = p1.makeFriends(p2)
    }
    //调用
    var p1 = new Student("zhang3")
    var p2 = new Student("li4")
    new Party[Student](p1, p2).play

    例2: 
    def compare[T <:Comparable[T]](first:T,second:T) = {//表示Comparable的子类
    if (first.compareTo(second)>0) 
    first
    else
    second
    }
    调用;:compare("A,"B”) //B

3) 视图界定

    用<%表示: 相当于上面的<:加上隐式转化

    class Person(val name: String) {
        def makeFriends(other: Person) = println(this.name + "和" + other.name + "交朋友")
    }
    class Teacher(var name: String)

    class Party[T <% Person](p1: T, p2: T) {
        def play = p1.makeFriends(p2)
    }

    implicit def teacher2Person(t: Teacher) : Person = new Person(t.name)

    //调用
    var p1 = new Teacher("zhang3")
    var p2 = new Teacher("li4")
    new Party[Teacher](p1, p2).play

4) 上下文界定

    回顾以前:
        >: 继承:要求是长辈
        <: 继承:要求是低辈
        <% 继承+隐式转换 
    这次
        T:A   T是一个泛型, A是个类,要求调用这个类的函数时,必须要隐式引入A[T]。

        举个例子:T是整数Int,A是Ordering(理解成排序comparator,系统有这个类),要求使用时候要引入隐式值Ordering[Int]

        class Test[T:Ordering](val a:T,val b:T) {
            def bigger(implicit myComparator:Ordering[T]) {
                if (myComparator.compare(a,b)>0)
                    println(a +":"+ myComparator.getClass)
                else
                    println(b)
            }
        }
        new Test(1,2).bigger    //2
        ----反编译----
        public class Test{
            private final Object a;
            private final Object b;
            public Test(Object a, Object b,){this.a = a;this.b = b; }

            public void bigger(Ordering myComparator){
                if (myComparator.compare(a(), b()) > 0)
                    System.out.println(a());
                else
                    System.out..println(b());
            }

    }
        new Test(1,2).bigger(系统自带的实现Int的Ordering对象)     

5) 多重界定

    (1) T:A:B  隐式引入A[T],B[T]
    (2) T<%A<%B 存在T到A,T到B的隐式转换
    (3) T1>:T<:T2 T1是T类型的超类,T2也是T类型的超类

    class A[T]
    class B[T]
    implicit val a = new A[String]
    implicit val b = new B[String]

    //1.当前作用域中,必须存在两个隐式值,类型为 A[T],B[T]
    def test[T:A:B](x:T) = println(x)  

    test("测试1")


    implicit def t2A[T](x:T) = new A[T]
    implicit def t2B[T](x:T) = new B[T]

    //2.当前作用域中,必须存在T到A,T到B的隐式转换
    def test2[T <% A[T] <% B[T]](x:T) = println(x)
    test2("测试2")

6) 协变与逆变

    //解决问题,如果Child是Father的子类,那么List<Child>是不是List<Father>的子类,答案:不是,但这会造成很多问题。
    但Scala只要灵活运用协变与逆变,就可以解决这个问题

    协变 +T  当A是B的子类, 那么func(A) 是func(B)的子类
    例:
        class Father
        class Child extends Father
        class Test[+T]

        var x = new Test[Father]
        var y = new Test[Child]

        //此事,x也是y的父亲
        x = y   //正确
        y = x //错误

    逆变 -T
        当A是B的子类, 那么func(A) 是func(B)的父类

===========================================================================
11 scala并发编程基础(讲义已经单独做成doc文档:讲义-Actor编程.docx)
1) 简介
(1) 并发和并行
并发:在一台处理器上“同时”处理多个任务,一个时刻只有一个任务跑
并行:在多台处理器上同时处理多个任务,一个时刻有有个任务跑。

    优化:
    并发:增加进程或者线程,纵向扩展
    并行:增加cpu,横向扩展

    Actor模型:可以同时解决纵向和横向扩展

猜你喜欢

转载自blog.csdn.net/qq_41577045/article/details/79852675