1. 程式人生 > >Scala之——Scala程式設計基礎

Scala之——Scala程式設計基礎

1. Scala 基礎

1.1.宣告變數

package com.lyz.scala

/**
  * Created by liuyazhuang
  */
object VariableDemo {
  def main(args: Array[String]) {
    //使用val定義的變數值是不可變的,相當於java裡用final修飾的變數
    val i = 1
    //使用var定義的變數是可變得,在Scala中鼓勵使用val
    var s = "hello"
    //Scala編譯器會自動推斷變數的型別,必要的時候可以指定型別
    //變數名在前,型別在後
    val str: String = "liuyazhuang"
  }
}

1.2.常用型別

Scala Java 一樣,有 7 種數值型別 ByteCharShortIntLongFloat Double(無包
裝型別)和一個
Boolean 型別

1.3.條件表示式

Scala 的的條件表示式比較簡潔,例如:
package com.lyz.scala

/**
  * Created by liuyazhuang.
  */
object ConditionDemo {
  def main(args: Array[String]) {
    val x = 1
    //判斷x的值,將結果賦給y
    val y = if (x > 0) 1 else -1
    //列印y的值
    println(y)

    //支援混合型別表示式
    val z = if (x > 1) 1 else "error"
    //列印z的值
    println(z)

    //如果缺失else,相當於if (x > 2) 1 else ()
    val m = if (x > 2) 1
    println(m)

    //在scala中每個表示式都有值,scala中有個Unit類,寫做(),相當於Java中的void
    val n = if (x > 2) 1 else ()
    println(n)

    //if和else if
    val k = if (x < 0) 0
    else if (x >= 1) 1 else -1
    println(k)
  }
}

1.4.塊表示式

package com.lyz.scala

/**
  * Created by liuyazhuang.
  */
object BlockExpressionDemo {
  def main(args: Array[String]) {
    val x = 0
    //在scala中{}中課包含一系列表示式,塊中最後一個表示式的值就是塊的值
    //下面就是一個塊表示式
    val result = {
      if (x < 0){
        -1
      } else if(x >= 1) {
        1
      } else {
        "error"
      }
    }
    //result的值就是塊表示式的結果
    println(result)
  }
}

1.5.迴圈

scala 中有 for 迴圈和 while 迴圈,用 for 迴圈比較多
for 迴圈語法結構: for (i <- 表示式/陣列/集合)
package com.lyz.scala

/**
  * Created by liuyazhuang.
  */
object ForDemo {
  def main(args: Array[String]) {
    //for(i <- 表示式),表示式1 to 10返回一個Range(區間)
    //每次迴圈將區間中的一個值賦給i
    for (i <- 1 to 10)
      println(i)

    //for(i <- 陣列)
    val arr = Array("a", "b", "c")
    for (i <- arr)
      println(i)

    //高階for迴圈
    //每個生成器都可以帶一個條件,注意:if前面沒有分號
    for(i <- 1 to 3; j <- 1 to 3 if i != j)
      print((10 * i + j) + " ")
    println()

    //for推導式:如果for迴圈的迴圈體以yield開始,則該迴圈會構建出一個集合
    //每次迭代生成集合中的一個值
    val v = for (i <- 1 to 10) yield i * 10
    println(v)

  }

}

1.6.呼叫方法和函式

Scala 中的+ - * / %等操作符的作用與 Java 一樣,位操作符 & | ^ >> <<也一樣。只是有
一點特別的:
這些操作符實際上是方法。例如:
a + b
是如下方法呼叫的簡寫:
a.+(b)
a
方法 b 可以寫成 a.方法(b)

1.7.定義方法和函式

1.7.1. 定義方法

方法的返回值型別可以不寫,編譯器可以自動推斷出來,但是對於遞迴函式,必須指定返回型別

1.7.2. 定義函式

1.7.3. 方法和函式的區別
在函數語言程式設計語言中,函式是“頭等公民”, 它可以像任何其他資料型別一樣被傳遞和操

案例:首先定義一個方法,再定義一個函式,然後將函式傳遞到方法裡面


package com.lyz.scala

/**
  * Created by Liuyazhuang
  */
object MethodAndFunctionDemo {
  //定義一個方法
  //方法m2引數要求是一個函式,函式的引數必須是兩個Int型別
  //返回值型別也是Int型別
  def m1(f: (Int, Int) => Int) : Int = {
    f(2, 6)
  }

  //定義一個函式f1,引數是兩個Int型別,返回值是一個Int型別
  val f1 = (x: Int, y: Int) => x + y
  //再定義一個函式f2
  val f2 = (m: Int, n: Int) => m * n

  //main方法
  def main(args: Array[String]) {

    //呼叫m1方法,並傳入f1函式
    val r1 = m1(f1)
    println(r1)

    //呼叫m1方法,並傳入f2函式
    val r2 = m1(f2)
    println(r2)
  }
}
1.7.4. 將方法轉換成函式(神奇的下劃線)

2. 陣列、對映、元組、集合

2.1.陣列

2.1.1. 定長陣列和變長陣列
package com.lyz.scala

import scala.collection.mutable.ArrayBuffer

/**
  * Created by Liuyazhuang
  */
object ArrayDemo {

  def main(args: Array[String]) {

    //初始化一個長度為8的定長陣列,其所有元素均為0
    val arr1 = new Array[Int](8)
    //直接列印定長陣列,內容為陣列的hashcode值
    println(arr1)
    //將陣列轉換成陣列緩衝,就可以看到原陣列中的內容了
    //toBuffer會將陣列轉換長陣列緩衝
    println(arr1.toBuffer)

    //注意:如果new,相當於呼叫了陣列的apply方法,直接為陣列賦值
    //初始化一個長度為1的定長陣列
    val arr2 = Array[Int](10)
    println(arr2.toBuffer)

    //定義一個長度為3的定長陣列
    val arr3 = Array("hadoop", "storm", "spark")
    //使用()來訪問元素
    println(arr3(2))

    //////////////////////////////////////////////////
    //變長陣列(陣列緩衝)
    //如果想使用陣列緩衝,需要匯入import scala.collection.mutable.ArrayBuffer包
    val ab = ArrayBuffer[Int]()
    //向陣列緩衝的尾部追加一個元素
    //+=尾部追加元素
    ab += 1
    //追加多個元素
    ab += (2, 3, 4, 5)
    //追加一個數組++=
    ab ++= Array(6, 7)
    //追加一個數組緩衝
    ab ++= ArrayBuffer(8,9)
    //列印陣列緩衝ab

    //在陣列某個位置插入元素用insert
    ab.insert(0, -1, 0)
    //刪除陣列某個位置的元素用remove
    ab.remove(8, 2)
    println(ab)

  }
}
2.1.2. 遍歷陣列
1.增強 for 迴圈
2.好用的 until 會生成腳標, 0 until 10 包含 0 不包含 10
package com.lyz.scala

/**
  * Created by Liuyazhuang
  */
object ForArrayDemo {

  def main(args: Array[String]) {
    //初始化一個數組
    val arr = Array(1,2,3,4,5,6,7,8)
    //增強for迴圈
    for(i <- arr)
      println(i)

    //好用的until會生成一個Range
    //reverse是將前面生成的Range反轉
    for(i <- (0 until arr.length).reverse)
      println(arr(i))
  }
}
2.1.3. 陣列轉換
yield 關鍵字將原始的陣列進行轉換會產生一個新的陣列,原始的陣列不變

package com.lyz.scala

/**
  * Created by Liuyazhuang
  */
object ArrayYieldDemo {
  def main(args: Array[String]) {
    //定義一個數組
    val arr = Array(1, 2, 3, 4, 5, 6, 7, 8, 9)
    //將偶數取出乘以10後再生成一個新的陣列
    val res = for (e <- arr if e % 2 == 0) yield e * 10
    println(res.toBuffer)

    //更高階的寫法,用著更爽
    //filter是過濾,接收一個返回值為boolean的函式
    //map相當於將陣列中的每一個元素取出來,應用傳進去的函式
    val r = arr.filter(_ % 2 == 0).map(_ * 10)
    println(r.toBuffer)

  }
}
2.1.4. 陣列常用演算法
Scala 中,陣列上的某些方法對陣列進行相應的操作非常方便!

2.2.對映

Scala 中,把雜湊表這種資料結構叫做對映
2.2.1. 構建對映

2.2.2. 獲取和修改對映中的值


好用的 getOrElse

注意:在 Scala 中,有兩種 Map,一個是 immutable 包下的 Map,該 Map 中的內容不可變;
另一個是
mutable 包下的 Map,該 Map 中的內容可變
例子:

注意:通常我們在建立一個集合是會用 val 這個關鍵字修飾一個變數(相當於 java 中的 final),
那麼就意味著該變數的引用不可變,該引用中的內容是不是可變,取決於這個引用指向的集
合的型別

2.3.元組

對映是 K/V 對偶的集合,對偶是元組的最簡單形式,元組可以裝著多個不同型別的值。

2.3.1. 建立元組


2.3.2. 獲取元組中的值


2.3.3. 將對偶的集合轉換成對映


2.3.4. 拉鍊操作

zip 命令可以將多個值繫結在一起

注意:如果兩個陣列的元素個數不一致,拉鍊操作後生成的陣列的長度為較小的那個陣列的
元素個數

2.4.集合

Scala 的集合有三大類:序列 Seq、集 Set、對映 Map,所有的集合都擴充套件自 Iterable 特質
Scala 中集合有可變(mutable)和不可變(immutable)兩種型別, immutable 型別的集合
初始化後就不能改變了(注意與
val 修飾的變數進行區別)

2.4.1. 序列

不可變的序列 import scala.collection.immutable._
Scala 中列表要麼為空(Nil 表示空列表)要麼是一個 head 元素加上一個 tail 列表。
9 :: List(5, 2) :: 操作符是將給定的頭和尾建立一個新的列表
注意
:: 操作符是右結合的,如 9 :: 5 :: 2 :: Nil 相當於 9 :: (5 :: (2 :: Nil))

package com.lyz.collect

object ImmutListDemo {

  def main(args: Array[String]) {
    //建立一個不可變的集合
    val lst1 = List(1,2,3)
    //將0插入到lst1的前面生成一個新的List
    val lst2 = 0 :: lst1
    val lst3 = lst1.::(0)
    val lst4 = 0 +: lst1
    val lst5 = lst1.+:(0)

    //將一個元素新增到lst1的後面產生一個新的集合
    val lst6 = lst1 :+ 3

    val lst0 = List(4,5,6)
    //將2個list合併成一個新的List
    val lst7 = lst1 ++ lst0
    //將lst1插入到lst0前面生成一個新的集合
    val lst8 = lst1 ++: lst0

    //將lst0插入到lst1前面生成一個新的集合
    val lst9 = lst1.:::(lst0)

    println(lst9)
  }
}

可變的序列import scala.collection.mutable._

package com.lyz.collect
import scala.collection.mutable.ListBuffer

object MutListDemo extends App{
  //構建一個可變列表,初始有3個元素1,2,3
  val lst0 = ListBuffer[Int](1,2,3)
  //建立一個空的可變列表
  val lst1 = new ListBuffer[Int]
  //向lst1中追加元素,注意:沒有生成新的集合
  lst1 += 4
  lst1.append(5)

  //將lst1中的元素最近到lst0中, 注意:沒有生成新的集合
  lst0 ++= lst1

  //將lst0和lst1合併成一個新的ListBuffer 注意:生成了一個集合
  val lst2= lst0 ++ lst1

  //將元素追加到lst0的後面生成一個新的集合
  val lst3 = lst0 :+ 5
}

2.5. Set

不可變的 Set
package com.lyz.collect
import scala.collection.immutable.HashSet

object ImmutSetDemo extends App{
  val set1 = new HashSet[Int]()
  //將元素和set1合併生成一個新的set,原有set不變
  val set2 = set1 + 4
  //set中元素不能重複
  val set3 = set1 ++ Set(5, 6, 7)
  val set0 = Set(1,3,4) ++ set1
  println(set0.getClass)
}
 
可變的 Set
package com.lyz.collect
import scala.collection.mutable

object MutSetDemo extends App{
  //建立一個可變的HashSet
  val set1 = new mutable.HashSet[Int]()
  //向HashSet中新增元素
  set1 += 2
  //add等價於+=
  set1.add(4)
  set1 ++= Set(1,3,5)
  println(set1)
  //刪除一個元素
  set1 -= 5
  set1.remove(2)
  println(set1)
}

2.6. Map

package com.lyz.collect
import scala.collection.mutable

object MutMapDemo extends App{
  val map1 = new mutable.HashMap[String, Int]()
  //向map中新增資料
  map1("spark") = 1
  map1 += (("hadoop", 2))
  map1.put("storm", 3)
  println(map1)

  //從map中移除元素
  map1 -= "spark"
  map1.remove("hadoop")
  println(map1)
}

3. 類、物件、繼承、特質

Scala 的類與 JavaC++的類比起來更簡潔,學完之後你會更愛 Scala!!!

3.1.

3.1.1. 類的定義
//在Scala中,類並不用宣告為public。
//Scala原始檔中可以包含多個類,所有這些類都具有公有可見性。
class Person {
  //用val修飾的變數是隻讀屬性,有getter但沒有setter
  //(相當與Java中用final修飾的變數)
  val id = "9527"

  //用var修飾的變數既有getter又有setter
  var age: Int = 18

  //類私有欄位,只能在類的內部使用
  private var name: String = "唐伯虎"

  //物件私有欄位,訪問許可權更加嚴格的,Person類的方法只能訪問到當前物件的欄位
  private[this] val pet = "小強"
}
3.1.2. 構造器
注意:主構造器會執行類定義中的所有語句
/**
  *每個類都有主構造器,主構造器的引數直接放置類名後面,與類交織在一起
  */
class Student(val name: String, val age: Int){
  //主構造器會執行類定義中的所有語句
  println("執行主構造器")

  try {
    println("讀取檔案")
    throw new IOException("io exception")
  } catch {
    case e: NullPointerException => println("列印異常Exception : " + e)
    case e: IOException => println("列印異常Exception : " + e)
  } finally {
    println("執行finally部分")
  }

  private var gender = "male"

  //用this關鍵字定義輔助構造器
  def this(name: String, age: Int, gender: String){
    //每個輔助構造器必須以主構造器或其他的輔助構造器的呼叫開始
    this(name, age)
    println("執行輔助構造器")
    this.gender = gender
  }
}
/**
  *構造器引數可以不帶val或var,如果不帶val或var的引數至少被一個方法所使用,
  *那麼它將會被提升為欄位
  */
//在類名後面加private就變成了私有的
class Queen private(val name: String, prop: Array[String], private var age: Int = 18){
  
  println(prop.size)

  //prop被下面的方法使用後,prop就變成了不可變得物件私有欄位,等同於private[this] val prop
  //如果沒有被方法使用該引數將不被儲存為欄位,僅僅是一個可以被主構造器中的程式碼訪問的普通引數
  def description = name + " is " + age + " years old with " + prop.toBuffer
}

object Queen{
  def main(args: Array[String]) {
    //私有的構造器,只有在其伴生物件中使用
    val q = new Queen("hatano", Array("蠟燭", "皮鞭"), 20)
    println(q.description())
  }
}

3.2.物件

3.2.1. 單例物件
Scala 中沒有靜態方法和靜態欄位,但是可以使用 object 這個語法結構來達到同樣的目的
1.存放工具方法和常量
2.高效共享單個不可變的例項
3.單例模式
package com.lyz.scala

import scala.collection.mutable.ArrayBuffer

/**
  * Created by Liuyazhuang.
  */
object SingletonDemo {
  def main(args: Array[String]) {
    //單例物件,不需要new,用【類名.方法】呼叫物件中的方法
    val session = SessionFactory.getSession()
    println(session)
  }
}

object SessionFactory{
  //該部分相當於java中的靜態塊
  var counts = 5
  val sessions = new ArrayBuffer[Session]()
  while(counts > 0){
    sessions += new Session
    counts -= 1
  }

  //在object中的方法相當於java中的靜態方法
  def getSession(): Session ={
    sessions.remove(0)
  }
}

class Session{

}
3.2.2. 伴生物件
Scala 的類中,與類名相同的物件叫做伴生物件,類和伴生物件之間可以相互訪問私有的
方法和屬性

package com.lyz.scala

/**
  * Created by Liuyazhuang.
  */
class Dog {
  val id = 1
  private var name = "liuyazhuang"

  def printName(): Unit ={
    //在Dog類中可以訪問伴生物件Dog的私有屬性
    println(Dog.CONSTANT + name )
  }
}

/**
  * 伴生物件
  */
object Dog {

  //伴生物件中的私有屬性
  private val CONSTANT = "汪汪汪 : "

  def main(args: Array[String]) {
    val p = new Dog
    //訪問私有的欄位name
    p.name = "123"
    p.printName()
  }
}
3.2.3. apply 方法
通常我們會在類的伴生物件中定義 apply 方法,當遇到類名(引數 1,...引數 n)apply 方法會
被呼叫

package com.lyz.scala

/**
  * Created by Liuyazhuang.
  */
object ApplyDemo {
  def main(args: Array[String]) {
    //呼叫了Array伴生物件的apply方法
    //def apply(x: Int, xs: Int*): Array[Int]
    //arr1中只有一個元素5
    val arr1 = Array(5)
    println(arr1.toBuffer)

    //new了一個長度為5的array,數組裡麵包含5個null
    var arr2 = new Array(5)
  }
}
3.2.4. 應用程式物件
Scala 程式都必須從一個物件的 main 方法開始,可以通過擴充套件 App 特質,不寫 main 方法
package com.lyz.scala

/**
  * Created by Liuyazhuang.
  */
object AppObjectDemo extends App{
  //不用寫main方法
  println("I love you Scala")
}

3.3.繼承

3.3.1. 擴充套件類
Scala 中擴充套件類的方式和 Java 一樣都是使用 extends 關鍵字
3.3.2. 重寫方法
Scala 中重寫一個非抽象的方法必須使用 override 修飾符
3.3.3. 型別檢查和轉換

Scala

Java

obj.isInstanceOf[C]

obj instanceof C

obj.asInstanceOf[C]

(C)obj

classOf[C]

C.class

3.3.4. 超類的構造
package com.lyz.scala

/**
  * Created by Liuyazhuang.
  */
object ClazzDemo {
  def main(args: Array[String]) {
    //val h = new Human
    //println(h.fight)
  }
}

trait Flyable{
  def fly(): Unit ={
    println("I can fly")
  }

  def fight(): String
}

abstract class Animal {
  def run(): Int
  val name: String
}

class Human extends Animal with Flyable{

  val name = "abc"

  //列印幾次"ABC"?
  val t1,t2,(a, b, c) = {
    println("ABC")
    (1,2,3)
  }

  println(a)
  println(t1._1)

  //在Scala中重寫一個非抽象方法必須用override修飾
  override def fight(): String = {
    "fight with 棒子"
  }
  //在子類中重寫超類的抽象方法時,不需要使用override關鍵字,寫了也可以
  def run(): Int = {
    1
  }
}

4. 模式匹配和樣例類

Scala 有一個十分強大的模式匹配機制,可以應用到很多場合:如 switch 語句、型別檢查等。
並且
Scala 還提供了樣例類,對模式匹配進行了優化,可以快速進行匹配

4.1.匹配字串

package com.lyz.cases
import scala.util.Random

object CaseDemo01 extends App{
  val arr = Array("YoshizawaAkiho", "YuiHatano", "AoiSola")
  val name = arr(Random.nextInt(arr.length))
  name match {
    case "YoshizawaAkiho" => println("吉澤老師...")
    case "YuiHatano" => println("波多老師...")
    case _ => println("真不知道你們在說什麼...")
  }
}

4.2.匹配型別

package com.lyz.cases
import scala.util.Random

object CaseDemo01 extends App{
  //val v = if(x >= 5) 1 else if(x < 2) 2.0 else "hello"
  val arr = Array("hello", 1, 2.0, CaseDemo)
  val v = arr(Random.nextInt(4))
  println(v)
  v match {
    case x: Int => println("Int " + x)
    case y: Double if(y >= 0) => println("Double "+ y)
    case z: String => println("String " + z)
    case _ => throw new Exception("not match exception")
  }
}
注意case y: Double if(y >= 0) => ...
模式匹配的時候還可以新增守衛條件。如不符合守衛條件,將掉入 case _中

4.3.匹配陣列、元組

package com.lyz.cases

object CaseDemo03 extends App{

  val arr = Array(1, 3, 5)
  arr match {
    case Array(1, x, y) => println(x + " " + y)
    case Array(0) => println("only 0")
    case Array(0, _*) => println("0 ...")
    case _ => println("something else")
  }

  val lst = List(3, -1)
  lst match {
    case 0 :: Nil => println("only 0")
    case x :: y :: Nil => println(s"x: $x y: $y")
    case 0 :: tail => println("0 ...")
    case _ => println("something else")
  }

  val tup = (2, 3, 7)
  tup match {
    case (1, x, y) => println(s"1, $x , $y")
    case (_, z, 5) => println(z)
    case  _ => println("else")
  }
}

注意:在 Scala 中列表要麼為空(Nil 表示空列表)要麼是一個 head 元素加上一個 tail 列表。
9 :: List(5, 2) :: 操作符是將給定的頭和尾建立一個新的列表

注意:: 操作符是右結合的,如 9 :: 5 :: 2 :: Nil 相當於 9 :: (5 :: (2 :: Nil))

4.4.樣例類

Scala 中樣例類是一中特殊的類,可用於模式匹配。 case class 是多例的,後面要跟構造參
數,
case object 是單例的
package com.lyz.cases
import scala.util.Random

case class SubmitTask(id: String, name: String)
case class HeartBeat(time: Long)
case object CheckTimeOutTask

object CaseDemo04 extends App{
  val arr = Array(CheckTimeOutTask, HeartBeat(12333), SubmitTask("0001", "task-0001"))

  arr(Random.nextInt(arr.length)) match {
    case SubmitTask(id, name) => {
      println(s"$id, $name")//前面需要加上s, $id直接取id的值
    }
    case HeartBeat(time) => {
      println(time)
    }
    case CheckTimeOutTask => {
      println("check")
    }
  }
}

4.5. Option 型別

Scala Option 型別樣例類用來表示可能存在或也可能不存在的值(Option 的子類有 Some
None)Some 包裝了某個值, None 表示沒有值
package com.lyz.cases

object OptionDemo {
  def main(args: Array[String]) {
    val map = Map("a" -> 1, "b" -> 2)
    val v = map.get("b") match {
      case Some(i) => i
      case None => 0
    }
    println(v)
    //更好的方式
    val v1 = map.getOrElse("c", 0)
    println(v1)
  }
}

4.6.偏函式

被包在花括號內沒有 match 的一組 case 語句是一個偏函式,它是 PartialFunction[A, B]的一個
例項,
A 代表引數型別, B 代表返回型別,常用作輸入模式匹配
package com.lyz.cases

object PartialFuncDemo  {

  def func1: PartialFunction[String, Int] = {
    case "one" => 1
    case "two" => 2
    case _ => -1
  }

  def func2(num: String) : Int = num match {
    case "one" => 1
    case "two" => 2
    case _ => -1
  }

  def main(args: Array[String]) {
    println(func1("one"))
    println(func2("one"))
  }
}