第1章 基础
  1. 调用函数和方法
  • 调用函数,以math包中的函数调用为例

    import scala.math._ "_"相当于java中的*

    pow(2, 4) 返回16.0。如果是scala开头的包,可以省略scala

  • 方法,scala中没有静态方法

    与之对应的是每一个类都有一个同名的伴生对象,定义在伴生对象中的方法可以直接调用

    类的构造方法可以使用伴生对象中的apply方法

    例如Array(1,2,3,4),其实调用的是Array.apply(1,2,3,4) 。

第2章 控制结构和函数
  1. 条件表达式

    val b:Any = if (condition) 1 else "abc"

    如果是if(condition) 1 <=> if (condition) 1 else () 返回类型是AnyVal

    Unit用"()"表示。AnyVal继承Any

  2. 语句的终止,可以使用Breaks,最好是使用while循环,通过标志位来终止循环

  3. 高级for循环和for推导式

    for循环中可以放多个生成式,每个生成式可以带一个守卫。

    例子: for(i<- 0 to 3 if i%2!=0; j<- 1 to 10 if j !=4)
    

    for循环中定义任意多的变量在循环中使用

    例子:for(i<- 0 to 3; from = 4 - i; j <- from to 10)
    

    for推导式

    例子: val cc=for(i<- 0 to 3) yield i*2   返回cc:Seq[Int]=Vector(0, 2, 4, 6)
    cc的类型和for中的第一生成式有关
    
  4. 函数

    函数定义,必须有=号。除非是递归调用的方式,不然不用声明返回值的类型

    def sum(a:Int,b:Int) = {
        a + b
    }
    +方式是Int隐式转换成RichInt之后的方法
    
    
  5. 可变参数

    可变参数
    def sum(args: Int*) = {
        //方法体里面使用的时候args变成了Seq
        //如果是递归调用,直接将args传进去则会失败
        sum(args.tail: _*)//将Seq转成可变参数传入
        //tail的意思是除第一个元素后的序列
    }
    
  6. 过程

    不带=号,没有返回值,那就是过程

    def process(i: Int){
        //这就是一个过程
    }
    
  7. 懒值,在使用的时候才进行初始化

    lazy val b=//操作开销比较大的资源
    
  8. 异常捕获

    try{
        //
    }catch{
        case e1:NPE => {}
        case _ => {}
    }
    //通过模式匹配的方式来捕获不同的异常
    
第3章 数组相关操作
  1. 定长数组

    Array("a","b")相当于java中的String[]

    Array[String](10) 长度为10的定长数组

    访问元素arr(0)

  2. 变长数组:数组缓冲

    ArrayBuffer("10","9")

    val s = ArrayBuffer[String]()

    数组尾端添加或者删除值都可以,且是一个高效的操作

    s += "abc"
    s ++= Array("g", "h")
    s.trimEnd(5)  尾端移除5个元素
    Array转ArrayBuffer和ArrayBuffer转Array
    s.toArray.toBuffer
    
  3. 遍历数组和缓冲数组

    for(elem <- elems)
    for (i <- 0 until elems.length)  遍历数组的索引
    for(i <- 0 until (elems.length,2)) 调着遍历索引
    for(i <- (0 until elems.length).reverse) 倒着遍历索引
    
  4. 练习,移除数组中第一个负数后面的所有负数

    代码逻辑:
    def main(args: Array[String]): Unit = {
        //移除数组中第一个负数之后的所有负数
        val ints = ArrayBuffer(1, -1, 1, 2, -3, 3, -2, -5, 0)
        var first = true
        //获取到所有的索引
        val indexes = for (i <- 0 until ints.length if first || ints(i) >= 0) yield {
          if (ints(i) < 0) first = false;i
        }
        //将满足条件的元素放到数组的前端,因为indexes(j)的值大于等于j,所以往前移的过程中,后面的值不会被覆盖
        for (j <- 0 until indexes.length) ints(j) = ints(indexes(j))
        ints.trimEnd(ints.length-indexes.length)
        println(ints)
      }
    
    
  5. 创建多维数组

        //创建规则的多维数组,3行4列的二维数组
        val doubleArray = Array.ofDim[Int](3,4)
        //创建不规则的多维数组,10行,每行数组的长度不确定的二维数组
        val arr = new Array[Array[Int]](10)
        for (i <- arr.indices) {
          arr(i) = Array[Int](i)
        }
        //i是形参,在for的代码块中是不允许变更的。下面这种方式是错误的
          for (elem <- arr) {
          elem = new Array[Int](9)
        }//编译失败的代码
        //因为没有apply方法适用,所以用new的方式创建
    
  6. scala和java的互转

       //集合的隐式转换,如果调用java的api需要传List<String>
        import scala.collection.JavaConversions.bufferAsJavaList
        val list=ArrayBuffer(1,2,3)
        list.add(5)//适用java集合的方法添加元素
       //java List转scala的Buffer
       import scala.collection.JavaConversions.asScalaBuffer
        val list1 = new util.ArrayList[String]()
        list1 += "a"
        println(list1)
    
第4章 映射和元组
  1. 映射的创建,可以创建hash映射和树映射

    创建不可变的映射
    Map("a"->1, "b"->2)
    创建可变的映射
    scala.collection.mutable.Map("a" -> 1, "b" -> 2)
    创建一个空的可变映射,需要指定映射的方式
    scala.collection.mutable.HashMap[String,Int]()
    
  2. 获取映射中的值

    map("a")直接获取的方式可能会报错
    Exception in thread "main" java.util.NoSuchElementException: key not found: c
    正确的调用方式,get()调用返回Option进行模式匹配Some或者None 或者getOrElse("a",3)给默认值
    
  3. 更新映射中的值

    为可变map添加值

       mm += ("b" -> 3)  //如果key已经存在,则修改
        mm -= "c"    //移除对应的key
        mm ++= Map("d" -> 4)
        mm --=Seq("d","a")
       
    
  4. 迭代映射

     for ((k,v) <- mm)
     反转映射
     val newmm=for((k,v)<- mm if k!="a") yield (v,k)
    
  5. 排序映射

    scala默认的是Hash映射,如果想要使用树结构的映射,需要key有序。scala只有一种不可变的Map

     val sortMap = scala.collection.immutable.SortedMap[Int,String](1->"c",-1->"d",5->"t",0->"y")
     
    还有一种方式,就是使用java的TreeMap来实现有序的映射。
    如果按照插入的顺序访问映射,使用scala.collection.mutable.LinkedHashMap
    
  6. 与java的互转

    import scala.collection.JavaConversions.mapAsScalaMap
    import scala.collection.JavaConversions.mapAsJavaMap
    
  7. 使用模式匹配获取元组的值

    //模式匹配的方式获取元组的值
        val t = (1, 2, "a", "b")
        val (f, second, _, _) = t
        _只是一个占位符,不使用
    
  8. 拉链操作

    Array("a","b").zip(Array(1,2)).toMap

第5章 类
  1. 简单的类和无参方法

    //scala不使用这种定义方式
    class Counter{
      private var i =0
      def increment(): Unit ={
        i+=1
      }
      def current=i
    }
    
    //改变状态的方法Increment
    //获取值的方法,去掉了()
    
    class Counter{
      var age=0
    }
    
    编译的时候自动创建setter和getter方法
    getter方法是age(),counter.age调用,省略了()
    setter方法是age_=(参数),counter.age_=(1)调用,也可以重新setter方法
    
    class Counter{
      private var privateAge:Int=0
      def age=privateAge
      def age_=(): Unit ={
        //set逻辑
      }
    }
    和java一样定义setter和getter方法
    

    如果把属性声明成private,则scala编译成java程序的时候setter和getter方法都声明成private的

    如果属性声明成val,只有getter方法生成

    如果不想要setter方法和getter方法,属性声明成private[this]

  2. 私有字段的访问

    private修饰符

    class Counter{
      private var privateAge:Int=0
      def age=privateAge
      def age_=(): Unit ={
        //set逻辑
      }
    }
    
    类中能访问对象的私有属性包括非this对象的私有属性
    class Counter{
      private var privateAge:Int=0
      def age=privateAge
      def age_=(): Unit ={
        //set逻辑
      }
      def compare(other:Counterr){
          other.privateAge也是能访问的
      }
    }
    
    如果只想当前对象能访问私有属性,则使用private[this]来进行修饰
    如果private[otherCounnter]修饰Counter的私有属性,如果指定其他类的类名,那么其他类中也可以访问Counter对象的私有属性
    
  3. scala类编程javaBean

     @BeanProperty
      var name:String=_
      为属性增加注解,自动生成setName方法和get方法,变量不能声明成private
      总共生成4个方法
      name
      name_=(name:String)
      setName(name:String)
      get()
      
    
  4. 辅助构造器

    class Counter(address:String){
      private var privateAge:Int=0
      privateAge = 1
      @BeanProperty
      var name:String=_
      def this(name:String,address:String){
        this(address)
        this.name=name
      }
      def this(name:String,other:String,address:String){
        this(name,address)
        this.name+=other
      }
    }
    
    辅助构造器必须调用主构造器,第二个辅助构造器其实也是调用了主构造器
    class Counter(val address:String,private var tt:String)
    主构造器中可以使用修饰符val、var、private,如果不声明,默认是val
    加了修饰符,参数会被声明成属性。
    如果没有修饰符,方法中使用了参数,也会被声称属性。如果没有使用,只是访问,不会被声明成字段
    
    禁止调用主构造器
    class Counter private(address:String)
    
    这样只能调用辅助构造器了嵌套类
    
    
  5. 嵌套类

    嵌套类,类中可以嵌套类,函数中可以嵌套函数,对象中可以嵌套类
    
第6章 对象
  1. 单例对象,定义一个object就是一个单例对象,对象在首次使用的时候构造出来

  2. 伴生对象,伴生对象和伴生类可以互相访问私有属性,前提是声明在同一个源文件中

    类访问伴生对象中的私有方法的时候,必须类名.方法名的方式调用,不能直接方法名调用,因为方法不在同一个域中
    
  3. 扩展类或特质的对象

    abstract class AbstractNothing(des:String){
      def a()
      def b()
    }
    
    object ImplNothing extends AbstractNothing("nothing")
    {
      override def a(): Unit = {println("aaa")}
    
      override def b(): Unit = {
        println("bbb")
      }
    }
    
    
    class Counter(address:String){
      private var privateAge:Int=0
      @BeanProperty
      var name:String=_
      def this(name:String,address:String){
        this(address)
        this.name=name
      }
      def this(name:String,other:String,address:String){
        this(name,address)
        this.name+=other
      }
    }
    
    object ImplNothing2 extends Counter("address")
    {
    //只能访问到非private的属性和方法
      ImplNothing2.name
      ImplNothing2.setName("a")
    }
    
  4. 枚举对象

    //枚举对象
    object EnumColor extends Enumeration {
    //  val GREEN, RED, BLUE = Value
      //调用value方法返回一个Value的实例
      val GREEN=Value(0,"abc")
      val RED=Value(1,"abc")
      val BLUE=Value(2,"abc")
      //为枚举实例定义id和name,如果没有定义id就是前面一个的id+1,如果前面没有那就从0开始
      //name没有定义那就是GREEN作为name
    }
    object TestH{
      def main(args: Array[String]): Unit = {
        val blue: EnumColor.Value = EnumColor.BLUE
      }
    }
    
第7章 包和引入
第8章 继承
  1. 类型检查和转换

    if (blue.isInstanceOf[EnumColor.Value]) {
          val newBlue=blue.asInstanceOf[EnumColor.Value]
        }
        
      //如果要准确判断是不是一个类的对象
        if (blue.getClass == classOf[Counter]) {
            
        }
        
       最优的方法是使用模式匹配,优于类型检查和转换
           blue match {
          case a: Counter => {
    
          }
          case _ => {
    
          }
        }
    
  2. 典型构造顺序和提前定义的问题

    object Demo02 {
      def main(args: Array[String]): Unit = {
        val et = new Class2()
        val arr = et.env
        println(arr.length) //打印的结果是0
      }
    
    }
    class Class1{
      val arraySize=10
      val env:Array[String]=new Array[String](arraySize)
    }
    
    class Class2 extends Class1{
      override  val arraySize=2
    }
    
    解释,先调用父类的构造方法,父类初始化arraySize,此时创建env,想要获取arraySize,调用方法arraySize(),但是这个方法已经被重写(重写了字段,就是重写了getter方法),这是调用子类的方法arraySize(),返回子类的arraySize,此时子类的arraySize还没有初始化成2。所有的int值,在分配
    空间的时候都会被初始化成0,所以得到的数组长度为0
    
    采用提前定义的方式来解决这个问题
    先初始化子类中的某些字段
    class Class2 extends {
      override val arraySize = 2
    } with Class1{
    }
    
  3. scala继承层级

    与java中基本类型对应的类和Unit类都是继承AnyVal
    其他类都是继承AnyRef,相当于java中的Object
    AnyRef和AnyVal都是继承自Any
    
    
    
第9章 文件和正则表达式
  1. 后面补充
第10章 特质
  1. 特质的定义方法(特质的意思是混入,将特质中的方法混入到类或者对象中)

    //定义一个有抽象方法的特质和具体实现方法的特质,抽象方法不用abstract,实现抽象方法不用使用override
    object Demo02 {
      def main(args: Array[String]): Unit = {
        val demo = new TestDemo
        demo.console()
      }
    }
    
    class TestDemo extends ImpLogger1 {
      def console(): Unit = {
        log("hello")
      }
    }
    
    trait Logger {
      def log(msg: String)
    
      def consoleLog(msg: String): Unit = {
        println("aaa")
      }
    }
    
    trait ImpLogger1 extends Logger {
      def log(msg: String): Unit = {
        //可以实现,也可以不实现特质中的抽象方法,必须要加override
        println(msg + "ImplLogger1")
      }
    }
    
    //定义一个特质对象,在对象上混入特质,前提是定义类的时候先混入特质。特质中的方法可以是空方法,但必须是实现方法,不能是抽象的。对象上混入特质时,可以使用过子特质混入,使用子特质的方法
    object Demo02 {
      def main(args: Array[String]): Unit = {
        val demo = new TestDemo with ImpLogger1
        demo.log("demo")
      }
    }
    
    class TestDemo extends Logger {
      def console(): Unit = {
        log("hello")
      }
    }
    
    trait Logger {
      def log(msg: String){}
    
      def consoleLog(msg: String): Unit = {
        println("aaa")
      }
    }
    
    trait ImpLogger1 extends Logger {
      override def log(msg: String): Unit = {
        //可以实现,也可以不实现特质中的抽象方法,必须要加override
        println(msg + "ImplLogger1")
      }
    }
    
    
    
  2. 特质的调用链,最右边是最小的,从最右边开始调用

    object Demo02 {
      def main(args: Array[String]): Unit = {
        val demo = new TestDemo
        demo.console()
      }
    }
    
    class TestDemo extends ConsoleLogger with TimestampLogger with ExtendLogger {
      def console(): Unit = {
        log("hello ")
      }
    }
    
    trait Logger {
      def log(msg: String){}
    }
    
    trait TimestampLogger extends Logger {
      override def log(msg: String): Unit = {
        //可以实现,也可以不实现特质中的抽象方法,必须要加override
        super.log(msg+"TimeStampLogger ")
      }
    }
    
    trait ExtendLogger extends Logger {
      override def log(msg: String): Unit = {
        super.log(msg+"ExtendLogger ")
      }
    }
    
    trait ConsoleLogger extends Logger{
      override def log(msg: String): Unit = {
        println("console:"+msg)
      }
    }
    输出结果:(无法通过super.log知道会调用哪个方法,具体调用的方法根据混入特质的顺序来确定。如果要指定调用哪个特质的方法,super[ConsoleLogger].log(msg)指定超类型来进行调用)
    console:hello ExtendLogger TimeStampLogger 
    

    1598876234625

  3. 将特质当做富接口来使用

    class TestDemo extends Logger {//混入了Logger特质之后都可以使用logger方法
      def console(): Unit = {
        debug("hello1")
        warn("w")
      }
    }
    
    trait Logger {
      def debug(msg: String) {
        println(msg)
      }
    
      def info(msg: String) {
        println("INFO: " + msg)
      }
    
      def warn(msg: String) {
        println("WARN: " + msg)
      }
    
      def error(msg: String) {
        println("ERROR: " + msg)
      }
    }
    
  4. 特质中的字段

    特质中的字段可以是抽象的也可以是具体的,初始化了的字段就是具体的。

  5. 特质的构造顺序,和特质方法的调用顺序是有区别的。

    • 超类先构造
    • 再构造特质,最后构造当前类
    • 构造特质的时候,先构造父特质。如果多个特质共用一个父特质,父特质也只被构造一次
    • 特质从左到右构造
    特质的构造是初始化特质中的字段和语句,不是方法
    extends 超类 with 特质1 with 特质2
    
    
  6. 特质可以约束只能混入某种类的子类

    只要加上this: 超类 =>

    下面都可以调用超类的方法,同时特质只能混入超类对应的子类中

    trait Logger {
      this: Exception =>
      def log() {
        println(getMessage)//可以直接调用Exception的方法,包括抽象方法
      }
    
      def log2(){
        println(getLocalizedMessage)
      }
    
      def debug(msg: String) {
        println(msg)
      }
    
      def info(msg: String) {
        println("INFO: " + msg)
      }
    
      def warn(msg: String) {
        println("WARN: " + msg)
      }
    
      def error(msg: String) {
        println("ERROR: " + msg)
      }
    }
    
  7. 特质只能混入包含特定方法的类

    class TestDemo extends Logger {
      def getMessage:String={
      "message"
      }
      def getLocalizedMessage:String={
      "localizedMessage"
      }
      def console(): Unit = {
        log()
        log2()
      }
    }
    
    trait Logger {
    //指定必须要实现的方法
      this: {def getMessage:String; def getLocalizedMessage:String} =>
      def log() {
        println(getMessage)
      }
    
      def log2() {
        println(getLocalizedMessage)
      }
    
      def debug(msg: String) {
        println(msg)
      }
    
      def info(msg: String) {
        println("INFO: " + msg)
      }
    
      def warn(msg: String) {
        println("WARN: " + msg)
      }
    
      def error(msg: String) {
        println("ERROR: " + msg)
      }
    }
    
第11章 操作符
  1. 一般的操作符都是左结合的,例如+-*/,也有是右结合的操作符

    a::b::Nil
    相当于a::(b::Nil)   创建了一个包含b的列表,再将a拼到列表的头部
    调用方式等价于 Nil.::(b)
    
  2. 类似函数的调用方法apply和update

        val map = new scala.collection.mutable.HashMap[String,Int]()
        map("user1")=100//类似函数的调用方式f(args)=value,f非函数,其实调用的就是f.update(args,value)
        Map("app1"->200)//类似函数的调用方式,f(args),f非函数,其实调用的就是f.apply(args)
    
第12章 高阶函数
  1. 使用函数进行排序

    "abc bc white hello a".split(" ").sortWith(_.length < _length)
    按照字符串的长度进行排序,顺序排序
    
  2. 闭包函数

    可以在变量不在作用域时被再调用

    //定义一个函数,可以生成不同函数的函数
    def mulBy(factor: Double) = (x: Double) => x * factor
        //生成一个乘以10的函数和一个乘以2的函数,传入mulBy的局部变量10被函数mulBy10继续使用,这就是闭包
    val mulBy10 = mulBy(10)
    val mulBy2 = mulBy(2)
    每个返回的函数都有自己的参数设置,mulBy就是一个闭包
    
  3. 柯里化

    //拆解需要多个参数的函数
        def mul(x: Double, y: Double) = x * y
        def mul1(x: Double) = (y: Double) => x * y
        val d = mul1(10)(11)
        val d1 = mul(10,11)
    
第13章 集合
  1. 序列

    • 可变序列(IndexdSeq)

      Vector(是ArrayBuffer的不可变版本,可以使用下标进行访问)和Range整数序列(只存储开始、结束、和步长)

    • 不可变序列

      ArrayBuffer

  2. 列表

    List,列表要么是一个空列表Nil,要么是一个head加一个tail,tail也是一个列表。

  3. 可变列表

    LinkedList,通过一个引用对其操作

    var linkedList = mutable.LinkedList[String]()
        linkedList:+="list"//对可变链表进行操作
    var cur = linkedList //用一个引用来遍历链表
    //判断链表是否为空
    while(cur != Nil)
    {
        //cur.elem获取当前元素
        cur = cur.next//指向下一个元素
    }
    
  4. 集set

    scala中没有不可变的排序集,Set(可变)是没有插入序的(基于hash实现)。如果要插入序保持不变,使用(可变)LinkedHashSet,如果要对set进行排序,使用SortedSet(不可变,底层是由红黑树来实现的)。如果需要可变的有序集,必须使用java的TreeSet。

  5. 各种集合添加和删除元素的方法。快学scala.pdf(p126)

    一般而言,+是将元素插入到无先后序的集合,+:和:+是将元素添加到有先后的集合的头部和尾部

    例如:

        var vec = Vector(1,2,3)
    //    vec(0)=1//相当于update(0,1)的操作,因为是不可变的序列,不能修改
        println(vec(0))
        val vec2 = vec:+5
        println(vec2)
        即使Vector是不可变,vec添加5组成的是一个新的Vector,不改变原来的序列
    
  6. 总结集合的操作

    • 向后:+或向前+:追加元素到有先后顺序的集合中
    • +追加元素到无先后顺序的集合中
    • 用-移除元素
    • 用++和--来批量添加和移除元素
    • 对于列表,优先使用::和:::
    • 改值操作有+=、++=、-=和--=
    • 对于集合,union、intersect、diff操作使用++、& 、--
    • 尽量不使用++:、+=:和++=:
  7. 化简、折叠、拉链、迭代器、流操作

    • 化简、折叠操作

          //使用reduceLeft来统计总数,化简
          val ll = List(1, 3, 4, 5, 7)
          val words = List("w", "w", "a", "b", "w", "a")
          val result: Int = ll.reduceLeft(_ + _) //相当于((1+3)+4)+5 ……
          val result2 = ll.reduceRight(_ - _)
          //相当于1-(3-(4-(5-7)))
          //折叠,柯里化的使用,第一个参数是初始值,第二个值是要传入的函数
          val result3 = ll.foldLeft(0)(_ + _)
          //结果和化简的reduceLeft一样
          //要统计单词的词频,并且将词频放到一个不可变Map中去
          val emptyMap = Map[String, Int]()
          val wordCount = words.foldLeft(emptyMap) {
            (x, y) => x + (y -> (x.getOrElse(y, 0) + 1))
          }
          println(wordCount)
          //使用/:符号来实现foldLeft操作,相当于words./:(map)(func)
          val charToInt = (Map[Char, Int]() /: "wwabwa") {
            //注意前面的特殊符号调用要使用()包起来
            (x, y) => x + (y -> (x.getOrElse(y, 0) + 1))
          }
          println(charToInt)
          val middleResult = (1 to 10).scanLeft(0)(_+_)
          println(middleResult)
          //结果Vector(0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55)。初始值0也是中间结果
      //使用/:简写方式来做foldLeft的操作,因为有:,所以是后面的list调用前面的初始值
      
    • 拉链操作

      zip

      zipAll,当两个集合长度不一样的时候pad补充。

      zipWithIndex

         //拉链操作 zip zipAll(可以为较短的集合补充默认值) zipWithIndex
          val ll = List(1, 3, 4, 5, 7)
          val words = List("w", "w", "a", "b", "w", "a")
          val word2IndexGroup = words.zipWithIndex
          //输出List((w,0), (w,1), (a,2), (b,3), (w,4), (a,5))
          println(word2IndexGroup)
          val ll2words = ll.zipAll(words,10,"zip")
          //输出List((1,w), (3,w), (4,a), (5,b), (7,w), (10,a))
          println(ll2words)
      
    • 迭代器

      iterator在scala中使用较少,有一种情况迭代器是比较适合的,Source.fromFile,当从文件中读取资源,内存不能存下资源的时候,可以使用迭代器的next。迭代器对集合而言是一个“懒”的替代品。

  8. 懒视图

  9. 与java集合互操作

    1599301971651

    1599301994023

  10. 线程安全的集合

    暂时不去使用scala的并发包,

  11. 并行集合

    暂时不使用scala的并发包

第14章 模式提配和样例类
  1. 当成switch使用,同时可以当成一个表达式使用,而不是语句

        val aa = 10
        var bb = 0
        //当成switch语句使用
        aa match {
          case 10 => bb = 1
          case _ => bb = -1
        }
        //和if一样当成表达式使用
        bb = aa match {
          case 10 => 1
          case _ => -1
        }
    
  2. 守卫

      bb = aa match {
          case 10 => 1
          case _ if aa == cc => 0
          case _ => -1
        }
        先匹配带守卫的case_语句,不满足的时候再匹配case_语句
    
  3. 模式中的变量

        val i = 'a'
        val dd = i match {
          case '+' => 1
          case '-' => 0
          case ch if Character.isDigit(ch) => Character.digit(ch, 10)
        }
        变量ch可以将i的获取,守卫和后面的语句都可以使用
        import scala.math._
         val dd = i match {
          case `Pi` => 1
          case '-' => 0
          case ch if Character.isDigit(ch) => Character.digit(ch, 10)
        }
        当常量Pi和case的变量封装冲突时,不要直接使用case Pi,可以将Pi用反引号引起来,代表它是常量
        
    
  4. 类型模式

    可以对表达式的类型进行匹配,使用这种方式可以取代isInstanceOf,asInstanceOf

        val j:Any = ""
        val ee = j match {
          case a: Int => a
          case b: String => Integer.parseInt(b)
          case _: BigInt => Int.MaxValue
          case _ => 0
        }
        //进行类型匹配的时候泛型类型是不能识别的,除了在Array[Int]可以识别
          //case _: Map[String, Int] => 1 不使用
          case _ : Map[_, _] => 2
          case _ : Array[Int] => 1
    
  5. 匹配数组、列表和元组

    • 匹配数组

          val k =Array(0,1)
          k match {
            case Array(0) => println("只包含0的数组")
            case Array(0,_*)=>println("以0开头的数组")
            case Array(x, y) => println(x + " " + y + "包含两个元素的数组")//对于两个Int类型拼接,可以x+""+y的方式
            case _=>println("someone")
          }
      
    • 匹配列表

          val l =List(0,1)
          l match {
            case 0 :: Nil =>println("只包含0的数组")
            case 0 :: tail =>println("以0开头的数组")
            case x :: y :: Nil =>println(x + " " + y + "包含两个元素的数组")
            case _ =>println("someone")
          }
      
    • 匹配元组

          val t=(2,9)
          t match {
            case (0,_)=>println("匹配元组的第一个元素为0且元组元素个数为2")
            case (x,_)=>println("匹配元组的第一个元素")
            case _=>
          }
      
  6. 提取器

    匹配其实使用的是提取器方法unapply和unapplySeq,unapply提取的是固定长度的对象,比如tuple。unapplySeq提取的是可变长度的序列,可长可短,比如Array和List

    1599304442380

  7. 变量声明中的模式

      val (o, p) = (1, 2)
      val (i, u) = BigInt(10) /% 3
      BigInt的方法,将商赋值给i,将余赋值给u
      //将Array中的第一、二个元赋值给变量,但是tuple不能使用_*。tuple是固定长度的
      val Array(a,b,_*)=Array(0,1,3,4,5)
    
  8. for表达式中的模式

        import scala.collection.JavaConversions.propertiesAsScalaMap
        //将java的properties转成scala的map
        val props = new Properties()
        for ((k, v) <- props if k.startsWith("global")) {}
        for (("conf", v) <- props) {}//遍历k为conf的所有键值对
    
  9. 样例类

        case class A(k1:String,k2:String)
        case class B(k1:String)
        val rr:Any=A("a","")
        rr match{
          case A(k,_)=>println(k+"样例类A")
          case B(k)=>println(k+"样例类B")
          case _=>println("nothing")
        }
        //样例类自带setter方法和getter方法,apply方法和unapply方法
    

    样例类的copy方法

    rr.copy(k1="cc")//更改样例类对象的属性
    
  10. case语句中的中置表示法

  11. 匹配嵌套结构

    使用较少

  12. Option类

        //Option是对象
        //Some是Option的样例子对象,None是Option的样例子对象
        val map = Map("a" -> 3)
        map.get("a") match {
          case Some(x) => println()
          case None => println("空")
        }
    
  13. 偏函数

    PartialFunction[A,B],A是偏函数的输入类型,B是偏函数的输出类型。偏函数并不是对所有数据都处理的函数,与之对应的是全函数,偏函数只处理能匹配上case类型的数据

        //偏函数的应用
        val f: PartialFunction[Char, Int] = {
          case '+' => 1
          case '-' => -1
        }
        //apply方法返回能匹配上的表达式
        val i1 = f.apply('+')
        //isDefinedAt,判断能不能匹配上偏函数
        val matched = f.isDefinedAt('-')
    
第17章 类型参数
  1. 协变和逆变

    • 泛型定义在类名或者方法名的后面

      协变[+B],Array[Person]定义的方法参数,传入Array[Student],编译会失败。定义class Pair[+T](val first:T, second: T),协变的类型参数

      def f(arr: Pair[Person]),调用方法就可以传入f(Pair[Student]),Pair[Student]就是Pair[Person]的子类。

    • 逆变,-T和协变相反

第21章 隐式转换和隐式参数
  1. 隐式转换

    隐式转换函数就是implicit声明的带有单个参数的函数

        implicit def int2Fraction(n: Int) = new TestImplicitClass(n)
        import com.test.Demo02._ //导入隐式转换函数
        2.readSelf()
        
        
    class TestImplicitClass(value:Int){
      def readSelf(): Unit ={
        println(value)
      }
    }
    

    隐式转换发生的条件

    • 当表达式的类型和预期的类型不一致时,sqrt(str)
    • 当对象访问一个不存在的方法时
    • 不发生隐式转换编译能通过时,不发生隐式转换
  2. 隐式参数

    当函数或者方法的参数带有implicit的时候,会找一个缺省值赋值给对应的参数,这个缺省值可以是当前作用域中implicit val和def修饰的变量和函数,也可以是伴生对象中的implicit val和def修饰的变量和函数。也可以是import 导入的implicit val和def修饰的变量和函数

      object Demo01 {
      implicit val defaultValue = "demo01 value"
      }
      
      object Demo02 {
      import com.test.Demo01._
      def readDefaultValue(b:String)(implicit v:String){
        //def readDefaultValue(implicit v:String,b:String)会报错,implicit后面只能接一个参数
        println(b+v)
      }
      
      def main(args: Array[String]): Unit = {
        readDefaultValue("hello")
        }
      }
    
posted on 2020-08-30 21:16  jeasonchen001  阅读(197)  评论(0编辑  收藏  举报