Scala語言基礎

Scala語言基礎

1 語言介紹

  • 他已經出生15年了,就像明星一樣,誰都不可能一開始就人氣爆棚粉絲無數,得慢慢混。
  • 成功原因:完全兼容java程式碼。
  • 身世介紹
    • Scala在2004年正式問世,他的爸爸是Martin Odersky,這位老頭同時也是Genenric Java的爸爸。神不神奇!Scala和Genenric Java居然是親兄弟。
    • Scala的全稱叫scalable,可伸縮的意思。說白了就是可以簡單又可以複雜。scala是個純正的面向對象語言,並且具備函數式編程特性,這也是他最吸引人的地方。
    • Scala之所以這麼受歡迎還有一個原因,那就是Spark。專為大規模數據處理而設計的快速通用的計算引擎,這正是spark的最好詮釋,關於spark的更多資訊請百度。spark就是Scala編寫的,所以想了解spark的優越性能及應用就必須得學習Scala。

2 變數和數據類型

2.1 注釋

  1. 單行注釋

//這是單行注釋

  1. 多行注釋

/*

這是多行注釋

*/

  1. 文檔注釋

/**

這是文檔注釋

*/

2.2 變數

2.2.1 變數聲明

變數聲明:var 變數名:類型 = 初始值

常量聲明:var 變數名:類型 = 初始值

Scala中可以不寫 ; —>一般情況我們都不寫,因為;本來就對程式碼沒有什麼意義;

Scala中有自動類型推斷,所以一般情況都可以省略,故可以寫成如下方式。

變數聲明:var 變數名 = 初始值

常量聲明:var 變數名 = 初始值

在函數式編程中,能用常量就用常量,因為多執行緒中變數會引起執行緒安全問題。

2.2.2 數據類型

  • Any:所以類的父類
    • AnyVal:值類型->類似Java中的基本類型
      • 在Java中基本類型int,對應Scala中的Int,注意不是包裝類,在Scala中認為都是對象,故首字母都是大寫
      • StringOps:在Java中String是引用類型,在Scala中是值類型,這個類對String進行了拓展,也可以直接使用Java的String類
      • Unit:對應Java中的Void,但是Void沒有返回值,而Unit有返回值:()
    • AnyRef:引用類型
      • Null:所有引用類型的子類,值為null
  • Nothing:所有類的子類
  • 如果需要使用值類型的初始值,需要使用 _ 如:var name:String = _

2.2.3 類型轉換

和Java一樣,Scala也分自動類型轉換和強制類型轉換

自動類型轉換規則和Java一樣,都是從範圍小類型轉換到範圍大類型

強制類型轉換和Java不一樣,Scala是調用方法的方式來完成轉換

  • 強制類型轉換和自動類型轉換
object Demo {
  def main(args: Array[String]): Unit = {
    //強制類型轉換
    val a = 10
    println(a.toDouble)//10.0

    /**
      *自動類型轉換
      * byte->short->int->long->float->double
      *        char->int->long->float->double
    */
    val b:Double = 10
    println(b)//10.0
    // 如下這種自動類型轉換會失敗,因為不能自動類型轉換不能大轉小
    // val c:Int = 10.0
  }
}

3 字元串轉換

object Demo1 {
  def main(args: Array[String]): Unit = {
    //name = zhangsan,age = 10 sal = 50.56
    printf("name = %s,age = %d sal = %.2f", "zhangsan", 10, 50.55555)

    //name = lisi,age = 30
    val name = "lisi"
    val age = 20
    println(s"name = ${name},age = ${age + 10}")

    /**
      * a
      * b
      * c
      */
    val s =
      """
        |a
        |b
        |c
      """.stripMargin
    println(s)
  }
}

4 If Else

Scala中沒有三元運算符,只能使用If Else來實現其功能,寫法和Java一樣

Scala中沒有switch

Scala中,任何語法結構都有類型和值,返回最後一行程式碼的值。如If Else,賦值語句,程式碼塊等等

Scala中 == 等價於Java中的equals()方法,eq 等價於Java中的 ==

object Demo2 {
  def main(args: Array[String]): Unit = {
    var i = 1
    if (true) {
      i = 2
    } else {
      i = 0
    }
    //2
    println(i)
  }
}
object Demo3 {
  def main(args: Array[String]): Unit = {
    val func = if (true) {
      1
      2
    } else {
      0
    }
    //2
    println(func)
  }
}
object Demo4 {
  def main(args: Array[String]): Unit = {
    val func = if (true) {
    }
    //()
    println(func)
  }
}

5 循環

while和do while和Java一樣

for不像Java,其實是一種遍歷

5.1 while

object Demo5 {
  def main(args: Array[String]): Unit = {
    var i = 1
    while (i < 5) {
      println(i)
      i += 1
    }
  }
}

5.2 do while

object Demo6 {
  def main(args: Array[String]): Unit = {
    var i = 1
    do {
      println(i)
      i -= 1
    } while (i > 1)
  }
}

5.3 for

object Demo7 extends App {
  //遍歷序列
  for (x <- "123456") {
    println(x)
  }
  //間隔,反轉:列印10到1,並且間隔2個列印
  for (y <- 1.to(10, 2).reverse) {
    println(y)
  }
  //循環守衛:列印1到10的偶數
  for (z <- 1.to(10) if z % 2 == 0) {
    println(z)
  }
  //for推到:使循環可以返回最後的值,如下1到10的平方
  val a = for (z <- 1.to(10)) yield z * z
  println(a)
}
import scala.util.control.Breaks._

object Demo8 extends App {
  //退出循環
  breakable(
    for (x <- 1.to(10)) {
      if (x == 5) break()
      println(x)
    }
  )
}

6 Lambda及函數

函數式編程強調執行的結果而非執行過程,倡導利用若干簡單的單元讓計算結果不斷演進,逐層推到複雜運算,而不是設計一個複雜的運算過程。

在Scala中,函數也是一個對象,會有返回值,可以作為值傳遞給另外的函數。

函數的定義: def 函數名(參數1:參數1類型,參數2:參數2類型):函數返回值 = { 程式碼塊 }

函數返回值是可以自動推到的: def 函數名(參數1:參數1類型,參數2:參數2類型)= { 程式碼塊 }

函數形參都是val常量

  • 純函數

    1. 不能產生副作用

      • 函數不會產生副作用,除了返回值外,不修改程式的外部狀態(如修改全局變數,入參等)
      • 常見副作用:改變外部變數的值、向磁碟寫入數據、將頁面的一個按鈕設置為能否點擊
    2. 引用透明

      • 函數的運行不依賴於外部的變數或狀態,簡單的理解為多次調用,不會因為外部值而改變返回結果。
      • 天然適合併編程,因為調用函數的結果具有一致性,所以根本不需要加鎖,也不存在死鎖
  //純函數
  def add(a: Int, b: Int): Unit = {
    a + b
  }
      
 //純非函數 
 def add1(a: Int, b: Int): Unit = {
    //破壞了純函數,產生副作用
    println("列印功能")
    a + b
  }
     
 //純非函數      
  var x = 0;
  def add2(a: Int, b: Int): Unit = {
    //破壞了純函數,產生副作用
    x = 100
    a + b
  }
  var x = 1;
  def add(a: Int, b: Int)= {
    //破壞了純函數
    a + b + x
  }
  println(add(1, 2))    //4
  x = 10
  println(add(1, 2))    //13
  • 可變形參函數

參數類型*為可變形參,傳入一個序列中的每個元素。

  def add(a: Int*) = {
    a.sum
  }
  println(add(1, 2, 3))    //6
      
  def add(a: Int*) = {
    a.sum
  }
  val arr: Range.Inclusive = 1.to(3)
  println(add(arr: _*)) //6  序列展開需要使用: _*
      
  • 函數參數默認值
  def myconcat(x: String, pre: String = "<html>", suf: String = "</html>") = {
    pre + x + suf
  }

  //<html>abc</html>
  println(myconcat("abc", "<html>", "</html>"))
  println(myconcat("abc"))
  println(myconcat("abc", suf = "</html>"))

7 異常

同Java處理方式,有點小不同

import java.io.{FileInputStream, FileNotFoundException}

object Demo9 extends App {
  //在Java中FileNotFoundException屬於編譯時異常,必須處理,而Scala可以不處理,發生直接拋
  //new FileInputStream("")

  //java.io.FileNotFoundException:
  //處理完畢
  try {
    new FileInputStream("")
  } catch {
    case e: FileNotFoundException => println(e)
  }finally {
    println("處理完畢")
  }
}

8 對象

  1. Scala object相當於java中的單例,object中定義的全是靜態的,相當於java中的工具類,Object默認不可以傳參,對象要傳參,使用apply方法。

  2. Scala類中可以傳參,傳參一定要指定類型,有了參數就有了默認了構造。類中的屬性默認有getter和setter方法

  3. 類中重載構造時,構造中第一行必須先調用默認的構造 。def this(….){….}

  4. 在同一個scala文件中,class名稱和Object名稱一樣時,這個類叫做個對象的伴生類,這個對象叫做這個類的伴生對象,他們之間可以互相訪問私有變數。

8.1 主構造器

object Demo10 extends App {
  val user1 = new User("zhangsan", 15, "man")
  val user = new User( age=15, sex="man")

  /*set*/
  user.name = "lisi"
  //報錯  user.age=12
  //報錯  user.sex="女"

  /*get*/
  println(user.name)
  println(user.age)
  //報錯  println(user.sex)
}


class User(var name: String = "aa", val age: Int, sex: String) {
  /**
    * 構造函數及get set方法都自動生成了
    * var 變數      會在有參數的構造器中,get set方法都有
    * val 常量      會在有參數的構造器中,只有get方法
    * 無變數修飾符   會在有參數的構造器中,無get set方法
    * 可以傳遞默認值,由指定參數創建
    * 參數都是私有的屬性
    * 類自身就是public的,而且不限個數
    **/
  //主構造器,這裡面寫的程式碼,都會在對象創建的時候進行
  println("我生成了")
}

8.2 輔助構造器

object Demo11 extends App {
  val user = new User(15)
  println(user.name)
  println(user.age)
}


class User(var name: String = "aa", val age: Int, sex: String) {

  //輔助構造器,可以定義很多,類似於java的重載
  def this(username: String) = {
    //輔助構造器參數只能在當前輔助構造函數內當做普通的常量使用
    //第一個輔助構造的首行必須是調用主構造
    this(username, 10, "男")
  }

  def this(age: Int) {
    //可以調用前面的輔助構造器
    //this(name, age, "男")
    this("lisi")
  }
}

8.3 繼承

object Demo12 extends App {
  val zhangsan = new Son("zhangsan", 12)
  println(zhangsan.name)
  println(zhangsan.add(1, 2))
}

class Father(val name: String, val age: Int) {
  def add(a: Int, b: Int): Int = {
    a + b
  }
}

/**
  * 屬性複寫:
  * 如果子類屬性名和父類屬性名一樣,主類屬性被隱藏
  * 可以選擇override複寫主類屬性
  * 被複寫的屬性只能是val的
  * 未指定val,var的屬性,本身就是私有的,和繼承無關
  * 方法複寫:
  * 使用override關鍵字
  **/
class Son(override val name: String, override val age: Int) extends Father(name, age) {
  override def add(a: Int, b: Int): Int = {
    a + b + 1
  }
}

8.4 抽象類

object Demo13 extends App {
  val son = new Son()
  println(son.a)
  println(son.add(1, 2))
  //多態,強轉
  val father: Father = new Son
  if (father.isInstanceOf[Son]) {
    val s: Son = father.asInstanceOf[Son]
    println(s.a)
  }
  //匿名內部類
  println(new Father {
    override var a: Int = 1

    override def add(a: Int, b: Int): Int = {
      1
    }
  }.a)
}

/**
  * 抽象類:
  * 使用abstract關鍵字
  **/
abstract class Father() {
  //抽象欄位
  var a: Int

  //抽象方法
  def add(a: Int, b: Int): Int
}

class Son extends Father {
  override var a: Int = _

  override def add(a: Int, b: Int): Int = {
    a + b
  }
}

8.5 伴生類和伴生對象

object Demo14 extends App {
  //使用new關鍵字,直接走類的構造器
  val zhangsan = new Father("zhangsan", 12)
    
  //不適用new關鍵字,走伴生對象的apply()方法
  val lisi = Father("lisi", 20) //進入apply()方法
    
  println(zhangsan) //name:zhangsan,age:12
  println(lisi) //name:lisi,age:20
}

//Father對象的伴生類
class Father(val name: String, val age: Int) {
  override def toString: String = s"name:${name},age:${age}"
}

//Father類的伴生對象
object Father {
  //伴生對象中使用apply()方法,創建該對象的時候會調用該方法
  def apply(name: String, age: Int): Father = {
    println("進入apply()方法")
    new Father(name, age)
  }
}

8.6 特質Trait

Scala中的trait非常類似於Java中的interface,主要為了解決單繼承的問題。

一個類實現多個trait時,第一個關鍵字使用 extends,之後使用with

object Demo15 extends App {
  val son = new Son
  son.walk()	//走路
  son.eat()		//吃飯
}

trait Features {
  def walk()
}

trait Hobby {
  def eat()
}

class Son extends Features with Hobby {
  override def walk(): Unit = {
    println("走路")
  }

  override def eat(): Unit = {
    println("吃飯")
  }
}

8.7 隱式轉換

隱式轉換是在Scala編譯器進行類型匹配時,如果找不到合適的類型,那麼隱式轉換會讓編譯器在作用範圍內自動推導出來合適的類型,簡單的說就是:把任何東西,轉換成我們需要的格式。

  1. 隱式函數

名字無所謂,作用域範圍有效,在使用到相關功能的時候,找到是否有相關的隱式函數,有就直接使用。

注意:隱式轉換函數只與函數的參數類型和返回類型有關,與函數名稱無關,所以作用域內不能有相同的參數類型和返回類型的不同名稱隱式轉換函數。

object Demo16 extends App {
  implicit def double2Double(num: Double): Int = num.toInt

  var a: Int = 10.1
  println(a) //10

  implicit def father2Son(father: Father): Son = father.toSon

  var son: Son = new Father
  son.speakDad  //Dad
}

class Father {
  def toSon: Son = {
    new Son
  }
}

class Son {
  def speakDad {
    println("Dad")
  }
}
  1. 隱式值與隱式參數

隱式值是指在定義參數時前面加上implicit。隱式參數是指在定義方法時,方法中的部分參數是由implicit修飾【必須使用柯里化的方式,將隱式參數寫在後面的括弧中】。隱式轉換作用就是:當調用方法時,不必手動傳入方法中的隱式參數,Scala會自動在作用域範圍內尋找隱式值自動傳入。

隱式值和隱式參數注意:

  • 隱式值和隱式參數一起使用

  • 同類型的參數的隱式值只能在作用域內出現一次,同一個作用域內不能定義多個類型一樣的隱式值。

  • implicit 關鍵字必須放在隱式參數定義的開頭

  • 一個方法只有一個參數是隱式轉換參數時,那麼可以直接定義implicit關鍵字修飾的參數,調用時直接創建類型不傳入參數即可。

  • 一個方法如果有多個參數,要實現部分參數的隱式轉換,必須使用柯里化這種方式,隱式關鍵字出現在後面,只能出現一次

object Demo17 extends App {

  implicit val zs: String = "zhangsan"
  implicit val sr: Int = 100

  def printStudent(age: Int)(implicit name: String, score: Int) = {
    println(s"student :$name ,age = $age ,score = $score")
  }

  def printTeacher(implicit name: String) = {
    println(s"teacher name is = $name")
  }


  printStudent(18)     //student :zhangsan ,age = 18 ,score = 100
  printTeacher  	 //teacher name is = zhangsan
  printTeacher("lisi")  //teacher name is = lisi
}
  1. 隱式類

使用implicit關鍵字修飾的類就是隱式類。若一個變數A沒有某些方法或者某些變數時,而這個變數A可以調用某些方法或者某些變數時,可以定義一個隱式類,隱式類中定義這些方法或者變數,隱式類中傳入A即可。

隱式類注意:

  • 隱式類必須定義在類,包對象,伴生對象中。(必須是內部的)
  • 隱式類的構造必須只有一個參數,同一個類,包對象,伴生對象中不能出現同類型構造的隱式類。
object Demo18 extends App {

  implicit class Father(son: Son) {
    def speak() = {
      println(son.name + " You Are Excellent...")
    }
  }

  val f: Father = new Son("lisi")
  f.speak() //lisi You Are Excellent...
}

class Son(val s: String) {
  var name = s
}

9 集合

Scala中支援不可變集合和可變集合,不可變集合可以安全的並發訪問

可變集合:scala.collection.mutable 更改原集合

不可變集合:scala.collection.immutable 不會更改原集合,只會返回一個新集合

Scala默認採用不可變集合

9.1 數組

方法 描述
def apply( x: T, xs: T* ): Array[T] 創建指定對象 T 的數組, T 的值可以是 Unit, Double, Float, Long, Int, Char, Short, Byte, Boolean。
def concat[T]( xss: Array[T]* ): Array[T] 合併數組
def copy( src: AnyRef, srcPos: Int, dest: AnyRef, destPos: Int, length: Int ): Unit 複製一個數組到另一個數組上。相等於 Java’s System.arraycopy(src, srcPos, dest, destPos, length)。
def empty[T]: Array[T] 返回長度為 0 的數組
def iterate[T]( start: T, len: Int )( f: (T) => T ): Array[T] 返回指定長度數組,每個數組元素為指定函數的返回值。
def fill[T]( n: Int )(elem: => T): Array[T] 返回數組,長度為第一個參數指定,同時每個元素使用第二個參數進行填充。
def fill[T]( n1: Int, n2: Int )( elem: => T ): Array[Array[T]] 返回二數組,長度為第一個參數指定,同時每個元素使用第二個參數進行填充。
def ofDim[T]( n1: Int ): Array[T] 創建指定長度的數組
def ofDim[T]( n1: Int, n2: Int ): Array[Array[T]] 創建二維數組
def ofDim[T]( n1: Int, n2: Int, n3: Int ): Array[Array[Array[T]]] 創建三維數組
def range( start: Int, end: Int, step: Int ): Array[Int] 創建指定區間內的數組,step 為每個元素間的步長
def range( start: Int, end: Int ): Array[Int] 創建指定區間內的數組
def tabulate[T]( n: Int )(f: (Int)=> T): Array[T] 返回指定長度數組,每個數組元素為指定函數的返回值,默認從 0 開始。
def tabulate[T]( n1: Int, n2: Int )( f: (Int, Int ) => T): Array[Array[T]] 返回指定長度的二維數組,每個數組元素為指定函數的返回值,默認從 0 開始。
  • 定長數組
object Demo19 extends App {
  //第一種方式
  val arr: Array[Int] = Array(10, 20, 30, 40)
  println(arr(0)) //10
  println(arr.mkString(",")) //10,20,30,40

  //10	20	30	40
  for (i <- arr) {
    print(i + "\t")
  }
  println()

  //10	20	30	40
  arr.foreach(x => print(x + "\t"))
  println()

  //第二種方式
  val arr2: Array[Int] = new Array(3)
  for (i <- 1.to(arr2.length)) {
    arr2(i-1) = i * i
  }
  println(arr2.mkString(",")) //1,4,9
}
  • 可變長度數組
import scala.collection.mutable.ArrayBuffer
object Demo20 extends App {
  val arr = ArrayBuffer[String]("a", "b", "c")
  arr.append("hello", "scala") //在元素末尾追加多個元素
  arr.+=("end1", "end2") //在元素末尾追加多個元素
  arr.+=:("start") //在開頭添加元素
  println(arr.mkString(",")) //start,a,b,c,hello,scala,end1,end2
}

另外還有++=(合併),–=(差集)

9.2 列表

方法 描述
def +(elem: A): List[A] 前置一個元素列表
def ::(x: A): List[A] 在這個列表的開頭添加的元素。
def :::(prefix: List[A]): List[A] 增加了一個給定列表中該列表前面的元素。
def ::(x: A): List[A] 增加了一個元素x在列表的開頭
def addString(b: StringBuilder): StringBuilder 追加列表的一個字元串生成器的所有元素。
def addString(b: StringBuilder, sep: String): StringBuilder 追加列表的使用分隔字元串一個字元串生成器的所有元素。
def apply(n: Int): A 選擇通過其在列表中索引的元素
def contains(elem: Any): Boolean 測試該列表中是否包含一個給定值作為元素。
def copyToArray(xs: Array[A], start: Int, len: Int): Unit 列表的副本元件陣列。填充給定的數組xs與此列表中最多len個元素,在位置開始。
def distinct: List[A] 建立從列表中沒有任何重複的元素的新列表。
def drop(n: Int): List[A] 返回除了第n個的所有元素。
def dropRight(n: Int): List[A] 返回除了最後的n個的元素
def dropWhile(p: (A) => Boolean): List[A] 丟棄滿足謂詞的元素最長前綴。
def endsWith[B](that: Seq[B]): Boolean 測試列表是否使用給定序列結束。
def equals(that: Any): Boolean equals方法的任意序列。比較該序列到某些其他對象。
def exists(p: (A) => Boolean): Boolean 測試謂詞是否持有一些列表的元素。
def filter(p: (A) => Boolean): List[A] 返回列表滿足謂詞的所有元素。
def forall(p: (A) => Boolean): Boolean 測試謂詞是否持有該列表中的所有元素。
def foreach(f: (A) => Unit): Unit 應用一個函數f以列表的所有元素。
def head: A 選擇列表的第一個元素
def indexOf(elem: A, from: Int): Int 經過或在某些起始索引查找列表中的一些值第一次出現的索引。
def init: List[A] 返回除了最後的所有元素
def intersect(that: Seq[A]): List[A] 計算列表和另一序列之間的多重集交集。
def isEmpty: Boolean 測試列表是否為空
def iterator: Iterator[A] 創建一個新的迭代器中包含的可迭代對象中的所有元素
def last: A 返回最後一個元素
def lastIndexOf(elem: A, end: Int): Int 之前或在一個給定的最終指數查找的列表中的一些值最後一次出現的索引
def length: Int 返回列表的長度
def map[B](f: (A) => B): List[B] 通過應用函數以g這個列表中的所有元素構建一個新的集合
def max: A 查找最大的元素
def min: A 查找最小元素
def mkString: String 顯示列表的字元串中的所有元素
def mkString(sep: String): String 顯示的列表中的字元串中使用分隔串的所有元素
def reverse: List[A] 返回新列表,在相反的順序元素
def sorted[B >: A]: List[A] 根據排序對列表進行排序
def startsWith[B](that: Seq[B], offset: Int): Boolean 測試該列表中是否包含給定的索引處的給定的序列
def sum: A 概括這個集合的元素
def tail: List[A] 返回除了第一的所有元素
def take(n: Int): List[A] 返回前n個元素
def takeRight(n: Int): List[A] 返回最後n個元素
def toArray: Array[A] 列表以一個數組變換
def toBuffer[B >: A]: Buffer[B] 列表以一個可變緩衝器轉換
def toMap[T, U]: Map[T, U] 此列表的映射轉換
def toSeq: Seq[A] 列表的序列轉換
def toSet[B >: A]: Set[B] 列表到集合變換
def toString(): String 列錶轉換為字元串
  • 不可變列表
object Demo21 extends App {
  val list = List(1, 2, 3, 4, 5)
  println(list(0))  //1

  //filter過濾
  val list1 = list.filter { x => x >= 3 }
  println(list1.mkString(","))  //3,4,5

  //count統計個數
  val value = list.count(x => x > 3)
  println("count統計結果:" + value) //count統計結果:2
}
  • 可變數組
import scala.collection.mutable.ListBuffer
object Demo22 extends App {
  val listBuffer: ListBuffer[Int] = ListBuffer[Int](1,2,3,4,5)
  listBuffer.append(6,7,8,9)//末尾追加元素
  listBuffer.+=(10)//末尾追加元素
  listBuffer.+=:(100)//在開頭加入元素

  listBuffer.foreach(x => print(x.toString + "\t"))
}

9.3 集合

方法 描述
def +(elem: A): Set[A] 為集合添加新元素,x並創建一個新的集合,除非元素已存在
def -(elem: A): Set[A] 移除集合中的元素,並創建一個新的集合
def contains(elem: A): Boolean 如果元素在集合中存在,返回 true,否則返回 false。
def &(that: Set[A]): Set[A] 返回兩個集合的交集
def &~(that: Set[A]): Set[A] 返回兩個集合的差集
def +(elem1: A, elem2: A, elems: A*): Set[A] 通過添加傳入指定集合的元素創建一個新的不可變集合
def ++(elems: A): Set[A] 合併兩個集合
def -(elem1: A, elem2: A, elems: A*): Set[A] 通過移除傳入指定集合的元素創建一個新的不可變集合
def addString(b: StringBuilder): StringBuilder 將不可變集合的所有元素添加到字元串緩衝區
def addString(b: StringBuilder, sep: String): StringBuilder 將不可變集合的所有元素添加到字元串緩衝區,並使用指定的分隔符
def apply(elem: A) 檢測集合中是否包含指定元素
def count(p: (A) => Boolean): Int 計算滿足指定條件的集合元素個數
def copyToArray(xs: Array[A], start: Int, len: Int): Unit 複製不可變集合元素到數組
def diff(that: Set[A]): Set[A] 比較兩個集合的差集
def drop(n: Int): Set[A]] 返回丟棄前n個元素新集合
def dropRight(n: Int): Set[A] 返回丟棄最後n個元素新集合
def dropWhile(p: (A) => Boolean): Set[A] 從左向右丟棄元素,直到條件p不成立
def equals(that: Any): Boolean equals 方法可用於任意序列。用於比較系列是否相等。
def exists(p: (A) => Boolean): Boolean 判斷不可變集合中指定條件的元素是否存在。
def filter(p: (A) => Boolean): Set[A] 輸出符合指定條件的所有不可變集合元素。
def find(p: (A) => Boolean): Option[A] 查找不可變集合中滿足指定條件的第一個元素
def forall(p: (A) => Boolean): Boolean 查找不可變集合中滿足指定條件的所有元素
def foreach(f: (A) => Unit): Unit 將函數應用到不可變集合的所有元素
def head: A 獲取不可變集合的第一個元素
def init: Set[A] 返回所有元素,除了最後一個
def intersect(that: Set[A]): Set[A] 計算兩個集合的交集
def isEmpty: Boolean 判斷集合是否為空
def iterator: Iterator[A] 創建一個新的迭代器來迭代元素
def last: A 返回最後一個元素
def map[B](f: (A) => B): immutable.Set[B] 通過給定的方法將所有元素重新計算
def max: A 查找最大元素
def min: A 查找最小元素
def mkString: String 集合所有元素作為字元串顯示
def mkString(sep: String): String 使用分隔符將集合所有元素作為字元串顯示
def product: A 返回不可變集合中數字元素的積。
def size: Int 返回不可變集合元素的數量
def splitAt(n: Int): (Set[A], Set[A]) 把不可變集合拆分為兩個容器,第一個由前 n 個元素組成,第二個由剩下的元素組成
def subsetOf(that: Set[A]): Boolean 如果集合A中含有子集B返回 true,否則返回false
def sum: A 返回不可變集合中所有數字元素之和
def tail: Set[A] 返回一個不可變集合中除了第一元素之外的其他元素
def take(n: Int): Set[A] 返回前 n 個元素
def takeRight(n: Int):Set[A] 返回後 n 個元素
def toArray: Array[A] 將集合轉換為數組
def toBuffer[B >: A]: Buffer[B] 返回緩衝區,包含了不可變集合的所有元素
def toList: List[A] 返回 List,包含了不可變集合的所有元素
def toMap[T, U]: Map[T, U] 返回 Map,包含了不可變集合的所有元素
def toSeq: Seq[A] 返回 Seq,包含了不可變集合的所有元素
def toString(): String 返回一個字元串,以對象來表示
  • 不可變集合
object Demo23 extends App {
  val set1 = Set(1, 2, 3, 4, 4)

  set1.foreach(x => print(x + "\t"))//1	2	3	4
  println()

  //1	2	3	4
  for (s <- set1) {
    print(s + "\t")
  }
}
  • 可變集合
import scala.collection.mutable.Set
object Demo24 extends App {
  val set1 = Set[Int](1,2,3,4,5)
  set1.add(100)
  set1.+=(1,210,300)

  set1.foreach(x=>print(x+"\t"))//1	300	100	5	2	3	210	4
}

9.4 Map

方法 描述
def ++(xs: Map[(A, B)]): Map[A, B] 返回一個新的 Map,新的 Map xs 組成
def -(elem1: A, elem2: A, elems: A*): Map[A, B] 返回一個新的 Map, 移除 key 為 elem1, elem2 或其他 elems。
def –(xs: GTO[A]): Map[A, B] 返回一個新的 Map, 移除 xs 對象中對應的 key
def get(key: A): Option[B] 返回指定 key 的值
def iterator: Iterator[(A, B)] 創建新的迭代器,並輸出 key/value 對
def addString(b: StringBuilder): StringBuilder 將 Map 中的所有元素附加到StringBuilder,可加入分隔符
def addString(b: StringBuilder, sep: String): StringBuilder 將 Map 中的所有元素附加到StringBuilder,可加入分隔符
def apply(key: A): B 返回指定鍵的值,如果不存在返回 Map 的默認方法
def clone(): Map[A, B] 從一個 Map 複製到另一個 Map
def contains(key: A): Boolean 如果 Map 中存在指定 key,返回 true,否則返回 false。
def copyToArray(xs: Array[(A, B)]): Unit 複製集合到數組
def count(p: ((A, B)) => Boolean): Int 計算滿足指定條件的集合元素數量
def default(key: A): B 定義 Map 的默認值,在 key 不存在時返回。
def drop(n: Int): Map[A, B] 返回丟棄前n個元素新集合
def dropRight(n: Int): Map[A, B] 返回丟棄最後n個元素新集合
def dropWhile(p: ((A, B)) => Boolean): Map[A, B] 從左向右丟棄元素,直到條件p不成立
def empty: Map[A, B] 返回相同類型的空 Map
def equals(that: Any): Boolean 如果兩個 Map 相等(key/value 均相等),返回true,否則返回false
def exists(p: ((A, B)) => Boolean): Boolean 判斷集合中指定條件的元素是否存在
def filter(p: ((A, B))=> Boolean): Map[A, B] 返回滿足指定條件的所有集合
def filterKeys(p: (A) => Boolean): Map[A, B] 返回符合指定條件的的不可變 Map
def find(p: ((A, B)) => Boolean): Option[(A, B)] 查找集合中滿足指定條件的第一個元素
def foreach(f: ((A, B)) => Unit): Unit 將函數應用到集合的所有元素
def init: Map[A, B] 返回所有元素,除了最後一個
def isEmpty: Boolean 檢測 Map 是否為空
def keys: Iterable[A] 返回所有的key/p>
def last: (A, B) 返回最後一個元素
def max: (A, B) 查找最大元素
def min: (A, B) 查找最小元素
def mkString: String 集合所有元素作為字元串顯示
def product: (A, B) 返回集合中數字元素的積。
def remove(key: A): Option[B] 移除指定 key
def retain(p: (A, B) => Boolean): Map.this.type 如果符合滿足條件的返回 true
def size: Int 返回 Map 元素的個數
def sum: (A, B) 返回集合中所有數字元素之和
def tail: Map[A, B] 返回一個集合中除了第一元素之外的其他元素
def take(n: Int): Map[A, B] 返回前 n 個元素
def takeRight(n: Int): Map[A, B] 返回後 n 個元素
def takeWhile(p: ((A, B)) => Boolean): Map[A, B] 返回滿足指定條件的元素
def toArray: Array[(A, B)] 集合轉數組
def toBuffer[B >: A]: Buffer[B] 返回緩衝區,包含了 Map 的所有元素
def toList: List[A] 返回 List,包含了 Map 的所有元素
def toSeq: Seq[A] 返回 Seq,包含了 Map 的所有元素
def toSet: Set[A] 返回 Set,包含了 Map 的所有元素
def toString(): String 返回字元串對象
  • 不可變Map
object Demo25 extends App {
  val map = Map[String, Int]("a" -> 100, "b" -> 200, ("c", 300), ("c", 400))
  val option1: Option[Int] = map.get("a")
  val option2: Option[Int] = map.get("aa")
  println("從map中取鍵為a的值\t"+option1)  //從map中取鍵為a的值	Some(100)
  println("從map中取鍵為a的值\t"+option1.get)  //從map中取鍵為a的值	100
  println("從map中取鍵為aa的值\t"+option2) //從map中取鍵為aa的值	None
  println("從map中取鍵為aa的值\t"+option2.getOrElse("no value")) //從map中取鍵為aa的值	no value


  println(map)  //Map(a -> 100, b -> 200, c -> 400)

  //(a,100)	(b,200)	(c,400)
  for (elem <- map) {
    print(elem + "\t")
  }
  println()


  val keys: Iterable[String] = map.keys
  //key = a ,value = 100	key = b ,value = 200	key = c ,value = 400
  keys.foreach(key => {
    val value = map.get(key).get
    print(s"key = $key ,value = $value\t")
  })
  println()

  val values: Iterable[Int] = map.values
  values.foreach(x => print(x + "\t"))  //100	200	400
  println()
}
  • 可變Map
import scala.collection.mutable.Map
object Demo26 extends App {

  val map2 = Map[String, Int]()

  map2.put("hello", 100)
  map2.put("world", 200)
  map2.put("hadoop", 200)
  println(map2.mkString("\t")) //hadoop -> 200	world -> 200	hello -> 100

  //count
  val countResult = map2.count(x => {
    x._1.equals("hadoop") && x._2 == 200
  })
  println(s"鍵為hadoop值為200的元素有 $countResult 個") //鍵為hadoop值為200的元素有 1 個

  //filter
  map2.filter(_._1 == "world").foreach(x => println((x._1, x._2))) //(world,200)

  //contains
  println("是否有鍵為hello的元素", map2.contains("hello"))  //(是否有鍵為hello的元素,true)

  //exist
  println("符合條件的元素存在么", map2.exists(x => {
    x._1.equals("hadoop")
  })) //(符合條件的元素存在么,true)
}

9.5 元組

object Demo27 extends App {
  val tuple1: Tuple1[String] = new Tuple1("hello")
  val tuple2: (String, Int) = new Tuple2("a", 100)
  val tuple3: (Int, Boolean, Char) = new Tuple3(1, true, 'C')
  val tuple4: (Int, Double, String, Boolean) = Tuple4(1, 3.4, "abc", false)
  val tuple6: (Int, Int, Int, Int, Int, String) = (1, 2, 3, 4, 5, "abc")
  val tuple22 = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, "abc", 14, 15, 16, 17, 18, 19, 20, 21, 22)

  println("取值:" + tuple22._13) //取值:abc
  println("輸出整個元組:" + tuple4)  //輸出整個元組:(1,3.4,abc,false)

  val iter: Iterator[Any] = tuple6.productIterator
  //1	2	3	4	5	abc
  while (iter.hasNext) {
    print(iter.next() + "\t")
  }
  println()
  
  //1	2	3	4	5	abc
  tuple6.productIterator.foreach(x => print(x + "\t"))
}

10 Scala高級

10.1 高階函數

能夠接收函數作為參數的函數,或者返回值是函數的函數,就是高階函數

object Demo28 extends App {
  def sumNum(num1: Int, num2: Int)(f: (Int, Int) => Int): Unit = {
    println(f(num1, num2))
  }

  sumNum(1, 2)(_ + _) 	//3
}

10.2 部分引用函數

在Scala中_充當佔位符

object Demo29 extends App {
  def add(num1: Int, num2: Int): Int = {
    num1 + num2
  }

   val intAdd: Int => Int = add(1,_)
  println(intAdd(1))  //2
  println(intAdd(2))  //3
  println(intAdd(3))  //4
}

10.3 柯里化

從單個參數列表,換成多個參數列表,簡化了函數的調用,主要配合隱式值使用。

object Demo30 extends App {
  def add(x: Int)(y: Int) = x + y
  println(add(1)(2)) //3

  implicit val a: Int = 10
  def implicitAdd(x: Int)(implicit y: Int) = x + y
  println(implicitAdd(1)) //11
}

10.4 Map映射

主要用來轉化結構

object Demo31 extends App {
  //val seq: IndexedSeq[String] = 1.to(10).map(x => x + "->")
  val seq: IndexedSeq[String] = 1.to(10).map(_ + "->")
  println(seq)  //Vector(1->, 2->, 3->, 4->, 5->, 6->, 7->, 8->, 9->, 10->)
}

10.5 Filter過濾

object Demo32 extends App {
  val list = List(10, 20, 30, 40)
  val filterList: List[Int] = list.filter(_ > 10)
  println(filterList.mkString(",")) //20,30,40
}

10.6 FlatMap平面化

object Demo33 extends App {
  val list: List[List[Int]] = List(List(1, 2), List(3, 4), List(5, 6))
  val fList: List[Int] = list.flatMap(list => list.map(_ * 2))
  println(fList) //List(2, 4, 6, 8, 10, 12)
}

10.7 Reduce聚合

object Demo34 extends App {
  val list = List(1, 2, 3, 4)
  val result: Int = list.reduce(_ + _)
  println(result) //10
}

10.8 FoldLeft左摺疊

Reduce加強版,如下wordCount案例

import scala.collection.mutable.Map
object Demo35 extends App {
  val list = List("a", "b", "c", "d", "a", "b", "c", "a", "b", "a")

  //寫法一
  val metaMap1: Map[String, Int] = Map[String, Int]()
  val foldMap1: Map[String, Int] = list.foldLeft(metaMap1)((mp, listElement) => {
    mp(listElement) = mp.getOrElse(listElement, 0) + 1
    mp
  })
  println(foldMap1)  //Map(b -> 3, d -> 1, a -> 4, c -> 2)

  //寫法二
  val metaMap2: Map[String, Int] = Map[String, Int]()
  val foldMap2: Map[String, Int] = (metaMap2 /: list) ((mp, listElement) => {
    mp(listElement) = mp.getOrElse(listElement, 0) + 1
    mp
  })
  println(foldMap2) //Map(b -> 3, d -> 1, a -> 4, c -> 2)
}

10.9 Scan掃描

保存摺疊過程中的結果

object Demo36 extends App {
  val list: List[Int] = 1.to(10).toList
  val result: List[Int] = list.scan(0)(_ + _)
  println(result) //List(0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55)
}

10.10 拉鏈

object Demo37 extends App {
  val list1: List[Int] = 1.to(10).toList
  val list2: List[Int] = 11.to(19).toList
  val tuples1: List[(Int, Int)] = list1.zip(list2)
  val tuples2: List[(Int, Int)] = list1.zipAll(list2, 100, 200)
  //如果數量不等,多的丟棄
  println(tuples1) //List((1,11), (2,12), (3,13), (4,14), (5,15), (6,16), (7,17), (8,18), (9,19))
  //如果數量不等,使用默認值補齊
  println(tuples2) //List((1,11), (2,12), (3,13), (4,14), (5,15), (6,16), (7,17), (8,18), (9,19), (10,200))

  val unzip: (List[Int], List[Int]) = tuples1.unzip
  println(unzip) //(List(1, 2, 3, 4, 5, 6, 7, 8, 9),List(11, 12, 13, 14, 15, 16, 17, 18, 19))
}

spark中數量不等,會拋異常

10.11 分組

object Demo38 extends App {
  val list = List("a", "b", "c", "d", "a", "b", "c", "a", "b", "a")
    
  val map: Map[String, List[String]] = list.groupBy(ele => ele)
  println(map) //Map(b -> List(b, b, b), d -> List(d), a -> List(a, a, a, a), c -> List(c, c))
    
  val result: Map[String, Int] = map.map(kv => (kv._1, kv._2.length))
  println(result) //Map(b -> 3, d -> 1, a -> 4, c -> 2)
}

10.12 排序

object Demo39 extends App {
  val list = List(1, 5, 8, 2, 3, 6)
  //sorted排序
  val slist1: List[Int] = list.sorted
  println(slist1) //List(1, 2, 3, 5, 6, 8)

  val slist2: List[Int] = list.sorted(Ordering.Int.reverse)
  println(slist2) //List(8, 6, 5, 3, 2, 1)

  val sonList = List(Son("zhangsan", 10), Son("lisi", 5), Son("wangwu", 15))
  implicit val sonOrdering = new Ordering[Son] {
    override def compare(x: Son, y: Son): Int = {
      x.age - y.age
    }
  }
  println(sonList.sorted) //List(name:lisi,age:5, name:zhangsan,age:10, name:wangwu,age:15)
  println(sonList.sorted(sonOrdering.reverse)) //List(name:wangwu,age:15, name:zhangsan,age:10, name:lisi,age:5)

  //sortWith排序
  val sortWithList: List[Int] = list.sortWith((x, y) => x < y)
  println(sortWithList) //List(1, 2, 3, 5, 6, 8)
  val sortWithSon: List[Son] = sonList.sortWith((x, y) => x.age > y.age)
  println(sortWithSon) //List(name:wangwu,age:15, name:zhangsan,age:10, name:lisi,age:5)

  //sortBy
  val sons1: List[Son] = sonList.sortBy(son=>(son.name.length))(Ordering.Int.reverse)
  println(sons1)  //List(name:zhangsan,age:10, name:wangwu,age:15, name:lisi,age:5)
  val sons2: List[Son] = sonList.sortBy(son=>(son.name,son.age))(Ordering.Tuple2(Ordering.String.reverse,Ordering.Int.reverse))
  println(sons2) //List(name:zhangsan,age:10, name:wangwu,age:15, name:lisi,age:5)

}

class Son(val name: String, val age: Int) {
  override def toString: String = s"name:${name},age:${age}"
}

object Son {
  def apply(name: String, age: Int): Son = new Son(name, age)
}

10.13 Stream

object Demo40 extends App {
  val list = List(1, 2, 3, 5, 6, 8)
  val stream: Stream[Int] = list.toStream
  println(stream.head) //1
  println(stream.tail) //Stream(2, ?)
  stream.tail.foreach(x => print(s"$x\t"))  //2	3	5	6	8
  println()
  stream.take(3).foreach(x => print(s"$x\t")) //1	2	3
}

10.14 View

object Demo41 extends App {
  val list = List(1, 2, 3, 5, 6, 8)
  val view = list.view
  println(view.head)  //1
  view.tail.foreach(x => print(s"$x\t"))  //2	3	5	6	8
}

10.15 切片

object Demo42 extends App {
  val list = 1.to(10).toList
  val slice1: List[Int] = list.slice(0, list.length - 1)
  val slice2: List[Int] = list.slice(1, list.length)
  val tuples: List[(Int, Int)] = slice1.zip(slice2)
  println(tuples) //List((1,2), (2,3), (3,4), (4,5), (5,6), (6,7), (7,8), (8,9), (9,10))
  val result: List[String] = tuples.map(tuples => tuples._1 + "->" + tuples._2)
  println(result) //List(1->2, 2->3, 3->4, 4->5, 5->6, 6->7, 7->8, 8->9, 9->10)
}

10.16 模式匹配

10.16.1 基礎模式匹配

object Demo43 extends App {

  def operator(x: Int, y: Int, opt: String): Any = {
    opt match {
      case "+" => x + y
      case "-" => x - y
      case "*" => x * y
      case "/" => x.toDouble / y.toDouble
      case _ => 
    }
  }

  println(operator(3, 2, "+")) //5
  println(operator(3, 2, "-")) //1
  println(operator(3, 2, "*")) //6
  println(operator(3, 2, "/")) //1.5
  println(operator(3, 2, "xxx")) //()
}

10.16.2 守衛模式匹配

object Demo44 extends App {

  def operator(x: Char, y: Char, opt: String): Any = {
    opt match {
      case "+" => x + y
      case "-" => x - y
      case "*" => x * y
      case "/" => x.toDouble / y.toDouble
      case _ if (!Character.isDigit(x) || !Character.isDigit(y)) => "數字傳遞錯誤"    //模式匹配守衛
      case _ => "error"
    }
  }

  println(operator('3', '2', "+")) //5
  println(operator('3', '2', "-")) //1
  println(operator('3', '2', "*")) //6
  println(operator('3', '2', "/")) //1.5
  println(operator('3', 'x', "xxx")) //數字傳遞錯誤
  println(operator('3', '2', "xxx")) //error
}

10.16.3 變數作用域

object Demo45 extends App {

  //z...  ch在自己的作用域中,當做變數來處理,那麼就可以匹配任意值,並賦值給ch
  val ch = 'a'
  'z' match {
    case 'a' => println("a...")
    case ch => println(ch + "...")
    case _ => println("_...")
  }

  //_...  當首字母大寫時,當做常量來處理,正常匹配流程
  val Ch = 'a'
  'z' match {
    case 'a' => println("a...")
    case Ch => println(Ch + "...")
    case _ => println("_...")
  }
}

10.16.4 類型匹配

object Demo46 extends App {
  /**
    * 20
    * a
    * 30
    * b
    * 40
    * c
    */
  val list = List(10, "a", 20, "b", 30, "c")

  for (elem <- list) {
    elem match {
      case n: Int => println(n + 10)
      case s: String => println(s)
      case _ =>
    }
  }

  //int array
  val arr: Array[_] = Array[Int]()
  arr match {
    case arr: Array[String] => println("string array")
    case arr: Array[Int] => println("int array")
  }
}

10.17 樣例類

使用了case關鍵字的類定義就是樣例類(case classes),樣例類是種特殊的類。實現了類構造參數的getter方法(構造參數默認被聲明為val),當構造參數是聲明為var類型的,它將幫你實現setter和getter方法。

樣例類默認幫你實現了apply,toString,equals,copy和hashCode等方法。

樣例類可以new, 也可以不用new

和模式匹配兼容性很好

object Demo47 extends App {
  val p1 = Father("lisi", 20)
  val p2 = Son("wangwu", 30)

  /**
    * father->name:lisi,age:20
    * son->name:wangwu,age:30
    */
  val list = List(p1, p2)
  list.foreach(x => {
    x match {
      case Father(name,age) => println(s"father->name:$name,age:$age")
      case Son(name,age) => println(s"son->name:$name,age:$age")
      case _ => println("no match")
    }
  })
}


abstract class Person()
case class Father(name: String, age: Int)extends Person
case class Son(name: String, age: Int) extends Person

10.18 偏函數

如果一個方法中沒有match 只有case,這個函數可以定義成PartialFunction偏函數。偏函數定義時,不能使用括弧傳參,默認定義PartialFunction中傳入一個值,匹配上了對應的case,返回一個值。

只會出現滿足的case,類似filter+map效果

11 Actor

  • Actor Model是用來編寫並行計算或分散式系統的高層次抽象(類似java中的Thread)讓程式設計師不必為多執行緒模式下共享鎖而煩惱,被用在Erlang 語言上, 高可用性99.9999999 % 一年只有31ms 宕機Actors將狀態和行為封裝在一個輕量的進程/執行緒中,但是不和其他Actors分享狀態,每個Actors有自己的世界觀,當需要和其他Actors交互時,通過發送事件和消息,發送是非同步的,非堵塞的(fire-andforget),發送消息後不必等另外Actors回復,也不必暫停,每個Actors有自己的消息隊列,進來的消息按先來後到排列,這就有很好的並發策略和可伸縮性,可以建立性能很好的事件驅動系統。
  • Actor的特徵:
    • ActorModel是消息傳遞模型,基本特徵就是消息傳遞
    • 消息發送是非同步的,非阻塞的
    • 消息一旦發送成功,不能修改
    • Actor之間傳遞時,自己決定決定去檢查消息,而不是一直等待,是非同步非阻塞的
  • 什麼是Akka
    • Akka 是一個用 Scala 編寫的庫,用於簡化編寫容錯的、高可伸縮性的 Java 和Scala 的 Actor 模型應用,底層實現就是Actor,Akka是一個開發庫和運行環境,可以用於構建高並發、分散式、可容錯、事件驅動的基於JVM的應用。使構建高並發的分散式應用更加容易。
    • spark1.6版本之前,spark分散式節點之間的消息傳遞使用的就是Akka,底層也就是actor實現的。1.6之後使用的netty傳輸。
  1. 案例一
import scala.actors.Actor
object Demo45 extends App {
  //創建actor的消息接收和傳遞
  val actor = new myActor()
  //啟動
  actor.start()
  //發送消息寫法
  /**
    * get String =i love you !
    * get Int
    * get default
    * */
  actor ! "i love you !"
  actor ! 500
  actor ! true
}

class myActor extends Actor {
  def act() {
    while (true) {
      receive {
        case x: String => println("get String =" + x)
        case x: Int => println("get Int")
        case _ => println("get default")
      }
    }
  }
}
  1. 案例2
import scala.actors.Actor
object Demo45 extends App {
  val actor1 = new Actor1()
  actor1.start()
  val actor2 = new Actor2(actor1)
  actor2.start()
  /**
    * i sava msg! = i love you !
    * i love you too !
    * could we have a date !
    * */
}

case class Message(actor: Actor, msg: Any)

class Actor1 extends Actor {
  def act() {
    while (true) {
      receive {
        case msg: Message => {
          println("i sava msg! = " + msg.msg)
          msg.actor ! "i love you too !"
        }
        case msg: String => println(msg)
        case _ => println("default msg!")
      }
    }
  }
}

class Actor2(actor: Actor) extends Actor {
  actor ! Message(this, "i love you !")
  def act() {
    while (true) {
      receive {
        case msg: String => {
          if (msg.equals("i love you too !")) {
            println(msg)
            actor ! "could we have a date !"
          }
        }
        case _ => println("default msg!")
      }
    }
  }
}