Scala語言學習

Scala語言學習

基礎

  • 安裝Scala
  • REPL(Read-Evaluate-Print-Loop shell)
  • 處理數據
    • 字面量:直接出現在源代碼中的數據
    • valueval <identifier>[: <type>] = <data>
      • 不可變的、有類型的存儲單元,支持類型推導
    • 變量variablevar <identifier>[: <type>] = <data>
      • 可變的、有類型的存儲單元,支持類型推導
      • 可重新賦值,但是不可改變數據類型(支持類型轉換的例外)
    • 命名:支持使用字母、數字和一些特殊的操作符字符
      • 例如可以使用*、:、+、\pi、\phi等,不能使用[].

      • 合法命名規則

        • 不以數字開頭
          • 以數字開頭的話,編譯器最初會把解析到的第一個數字當成一個字面量數字,然后后面解析到其他非數字字符就會出錯
        • 一個或多個除反引用號外的任意字符,這些字符必須包圍在一對反引號之中
        val `a.b` = 4
        
  • 類型
    • 核心數值類型

      • ByteShortIntLongFloatDouble
      • toType方法手動完成類型間轉換
      • JVM包裝數值類型可以互操作
    • 字符串String

      • 建立在JavaString基礎上

      • Java不同,==會檢查字符串真正的想等性,而不是對象引用想等性

      • 三重引號創建多行String,類似Python """ your string """

      • 字符串內插

        • 在字符串的第一個雙引號前面增加一個s前綴,使用美元符$指示插入數據的引用(某些容易區分的情況下,大括號{}可以省略,但是加上更容易區分)
        val approx = 355/113f
        println(s"Pi, using 355/133, is about ${approx}.")
        
      • 字符串內插的替代格式可以使用printf語法(java.util.Formatter

        • 在字符串前面添加f前綴
        scala> val item = "apple"
        scala> f"Enjoying this $item ${355/133.0}%.5f times today.")
        
      • 正則表達式(基于Javajava.util.regex.Pattern

        • 用正則表達式捕獲值val <Regex value>(<identifier>) = <input string>)
        • val pattern = """.* apple ([\d.]+) times .*""".r
        • val pattern(amountText) = input
        • val amount = amountText.toDouble
    • Scala類型概述

      Scala類型概述

      • Nothing:所有類型的子類
      • Null:所有指示null值的AnyRef類型的子類
      • Unit:指示沒有值(類似void
      • AnyScala中所有類型的根
      • AnyVal:所有值類型的根
      • AnyRef:所有引用(非值)類型的根
    • Scala不支持其他類型到Boolean類型的自動轉換。非null字符串不會計算為true

    • 元組 (<value 1>,<value 2>[,<value 3>...])

      • 不同于列表和數組,沒有辦法迭代處理一個元組中的元素,元組的作用只是作為多個值的容器
      • 根據元素的索引訪問元組中的元素(下劃線加上索引,索引從1開始)
      • 創建一個大小為2的元組,也可以使用關系操作符(->),表示元組中的鍵值對

表達式和條件式

  • 表達式塊(用{}結合多個表達式創建,最后一個表達式作為表達式塊的返回值)

    • 可嵌套、跨行,多個表達式寫在一行需要;分隔
  • 語句:不返回值的表達式

  • if...else表達式塊

    • if (<Boolean expression>) <expression>
    • if (<Boolean expression>) <expression> else <expression>
  • 匹配表達式(類似switch

    • Scala匹配表達式支持匹配如值、類型、正則表達式、數值范圍和數據結構內容
    <expression> match{
        case <pattern match> => <expression>
        [case ...]
    }
    
    • 可以使用模式替換式(pattern alternative),匹配多種內容
    • case <pattern 1> | <pattern 2> .. => <one or more expressions>
    • 增加通配模式,放止輸入內容無法匹配的情況運行時異常
      • 值綁定模式case <identifier> => <one or more expressions>
        • 將輸入綁定到一個特定的字面量上,如果不匹配就會進入這個綁定值的情況
      • 使用通配符 _:相當于匿名占位符,將在運行時替換為一個表達式的值
        • 與值綁定的不同在于不能再case表達式塊中訪問通配符的值
    • 用模式哨位匹配
      • 增加一個if語句,為匹配表達式增加條件邏輯
      • case <pattern> if <Boolean expression> => <one or more expression>
    • 用模式變量匹配類型
      • 匹配輸入表達式的類型
      • case <identifier>: <type> => <one or more expressions>
  • 循環

    • Range(范圍)數據結構

      • <starting integer> [to|until] <ending integer> [by increment]
    • 基本for循環迭代處理

      • for (<indentifier> <- <iterator>) [yield] [<expression>]
      • yield:如果指定,則調用的所有表達式的返回值將作為一個集合返回,如果沒有指定,則調用表達式,但是不能訪問它的返回值
      • for (x <- 1 to 7){println(s"Day $x:")}
    • 迭代器哨位(過濾器)

      • for (<indentifier> <- <iterator> if <Boolean expression>)...
      • threes = for(i <- 1 to 20 if i%3 == 0) yield i
    • 嵌套迭代器

      for{ x <- 1 to 2
           y <- 1 to 3 }
         { print(s"($x,$y) ")}
      
    • 值綁定

      • for (<indentifier> <- <iterator>; <identifier> = <expression>)...
      • for(i <- 0 to 8; pow = 1 <<i) yield pow
    • WhileDo/While循環

      • while(<Boolean expression>) statement

函數

  • 定義無輸入的函數
    • def <identifier> = <expression>
    • def <identifier>() = <expression>
    • 如果定義函數時沒加小括號,則調用時也不可以加小括號,而加了小括號的無輸入函數調用時可以不加小括號
  • 定義函數時指定返回類型 def <identifier>: <type> = <expression>
  • 定義函數 def <identifier>(<identifier>: <type>[,...]): <type> = <expression>
  • 過程:沒有返回值的函數
  • 使用表達式塊調用函數
    • 當使用一個參數調用函數時,可以使用表達式塊返回的結果發送參數
  • 遞歸函數
    • 遞歸函數可能會遇到“棧溢出”錯誤,Scala編譯器可以使用尾遞歸優化一些遞歸函數
    • 利用尾遞歸優化的函數,遞歸調用不會創建新的空間,而是使用當前函數的棧空間
    • 只有最后一個語句是遞歸調用的函數才能由Scala編譯器完成尾遞歸優化
    • 可以利用函數注解來標志一個函數將完成尾遞歸優化@annotation.tailrec
  • 嵌套函數
  • 用命名參數調用函數
    • 可以不按原先定義的順序指定參數
  • 有默認值的參數
  • Vararg參數
    • 這是一個函數參數,可以匹配調用者的0個或多個實參
    • vararg參數后面不能跟非vararg參數,因為無法加以區分
    • vararg參數實現為一個集合,可以通過迭代器訪問
    • 要標志一個參數匹配一個或者多個輸入實參,在函數定義中需要該參數類型后面增加一個*
    • 例如def sum(items: Int*): Int = {...}
  • 參數組
    • 可以把參數表分解為參數組,每個參數組用小括號分割
    • 例如def max(x:Int)(y:Int) = if (x > y) x else y
  • 類型參數
    • 類型參數指示了值參數類型或返回值類型
    • 函數參數或返回值的類型不再固定,而是可以由函數調用者設置
    • def <function-name>[type-name](<parameter-name>:<type-name>): <type-name>...
    • 例如def identity[A](a: A): A = a
  • 方法和操作符
    • 中綴點記法:<class instance>.<method>[(<parameters>)]
    • Scala中使用的所有算術運算符都是方法
    • Scala的操作符記法允許使用空格分隔對象、操作符方法和方法的參數(只有一個)
    • 例如2 + 32.+(3)是一樣的
    • 操作符記法:<object> <method> <parameter>
      • 如果要調用多個參數,則必須將多個參數包含在()

首類函數

  • “首類”表示函數不僅能得到聲明和調用,還可以作為一個數據結構用在這個語言的任何地方
  • 高階函數:接受其他函數作為參數,或者使用函數作為返回值
  • 聲明式編程:要求使用高階函數或其他機制聲明要做的工作,而不手動實現
  • 命令式編程:總要明確指定操作的邏輯流
  • 函數類型和值
    • 函數的類型是其輸入類型返回值類型的一個簡單組合,用一個箭頭從輸入類型指向輸出類型

    • ([<type>, ...]) => <type>

      def double(x: Int): Int = x*2
      val myDouble: (Int) => Int = double
      val myDoubleCopy = myDouble
      val myDouble2 = double _
      
      • myDouble就是一個值,只不過這個值可以調用
      • 一個函數值可以賦值給一個新值
      • myDouble必須有顯示的類型,以區分它是一個函數值,而不是一個函數調用(如果函數只有單個參數可以省略小括號)
      • val <identifier> = <function name> _:使用通配符為函數賦值
  • 函數字面量
    • 創建一個沒有名字的函數并把這個函數賦值一個新的函數值
    • val doubler = (x: Int) => x * 2
    • 函數字面量是所賦數據的一個字面量表達式
    • 其他名字:匿名函數Lambda表達式Lambdasfunction0,function1,function2,...(Scala編譯器對字面量的叫法。根據輸入參數的個數而定,單參數就叫<function1>)
    • 語法:([<identifier>: <type>, ...]) => <expression>
  • 占位符語法
    • 函數字面量的一種縮寫形式,將命名參數替換為通配符(_

    • 使用條件

      • 函數的現實類型在字面量之外指定(明確好輸入輸出)
      • 輸入參數最多只使用一次(因為只能按照位置匹配一次,多個占位符有著相同的符號,匹配之后就無法確定需要的參數)
    • 示例

      def combination(x: Int, y: Int, f: (Int,Int) => Int) = f(x,y)
      combination(23, 12, _*_)
      
      def tripleOp[A,B](a: A, b: A, c: A, f: (A, A, A) => B) => f(a, b, c)
      tripleOp[Int, Int](23, 92, 14, _ * _ + _)
      tripleOp[Int, Double](23, 92, 14, 1.0 * _ / _ / _)
      
  • 部分應用函數和柯里化
    • 功能:為函數保留一些重復使用的參數

    • 方法一:部分應用(partially apply)函數,使用通配符替代其他參數(函數按位置匹配)

      def factorOf(x: Int, y: Int) = y % x == 0
      val mutipleOf3 = factorOf(3, _: Int)
      val y = mutipleOf3(78)
      
    • 方法二:函數柯里化(curring,討好的意思),使用參數組

      • 有多個參數表的函數可以認為是多個函數的一個鏈。單個參數表則認為是一個單獨的函數調用

        def factorOf(x: Int)(y: Int) = y % x == 0
        val isEven = factorOf(2) _
        val z = isEven(32) // z = true
        
      • def factorOf(x: Int)(y: Int)的函數類型為 Int => Int => Boolean

  • 傳名參數
    • 一種函數參數類型:可以取一個值,也可以取最終返回一個值的函數

    • 語法:<identifier>: => <type>

      scala> def doubles(x: => Int) = {
           |     println("Now doubling" + x)
           |     x * 2
           | }
      doubles: (x: => Int)Int
      
      scala> doubles(5)
      Now doubling 5
      res1: Int = 10
      
      scala> def f(i: Int) = { println(s"Hello from f($i)"); i }
      f: (i: Int)Int
      
      scala> doubles( f(8) )
      Hello from f(8)
      Now doubling 8
      Hello from f(8) //doubles調用兩次x,所以f函數也調用兩次
      res2: Int = 16    
      
  • 偏函數
    • 有些函數并不能支持滿足輸入類型的所有可能值。這種函數稱為偏函數,因為它們只能部分應用于輸入數據
  • 用函數字面量塊調用高階函數
    • 實際上就是輸入的參數類型是函數,那個輸入參數可以用表達式塊或者函數字面量來代替
    • 好處
      • 將單獨的代碼塊包為在工具函數中
      • 管理數據庫事務,即高階函數打開會話,調用函數參數,然后commit或者rollback結束事務
      • 重新嘗試處理可能的錯誤,將函數參數調用指定的次數,直到不再產生錯誤
      • 根據局部、全局或者外部值有條件地調用函數參數

常用集合

  • 列表、集和映射
    • List:不可變的單鏈表
      • 訪問列表中的單個元素,可以作為一個函數調用這個列表,并提供一個索引號(從0開始)
      • List是一個不可變的遞歸數據結構(鏈表),列表中的每一項都有自己的表頭和越來越短的表尾
      • 所有列表都有一個Nil實例作為終結點,可以通過比較當前元素與Nil來檢查是否到達列表表尾(Nil實際上是List[Nothing]的一個單例實例
    • Set:不可變的無序集合
    • Map:不可變的鍵值庫,也稱為散列映射、字典或關聯數組
    • ListSetMap的根類型都是Iterable
      • foreach():取一個函數,對列表中的每一項,分別調用這個函數
      • map():取一個函數,將一個列表元素轉換為另一個值或類型
      • reduce():取一個函數,將兩個列表元素結合為一個元素
  • Cons操作符
    • 可以通過consconstruct的簡寫)操作符來構建列表

    • 使用Nil為基礎,并使用右結合的cons操作符::綁定元素,就可以構建列表

      val numbersOld = List(1,2,3)
      val numbers = 1 :: 2 :: 3 :: Nil
      
    • ::List的一個方法,它取一個值,這會成為新列表的表頭

      val first = Nil.::(1) // 1 -> Nil
      
  • 列表算術運算
    • 因為List是一個不可變的集合,所有“修改”是指“返回一個新列表,其中包含所請求的修改”
    • 謂詞函數(predicate function):得到一個輸入值后會相應地返回truefalse
    • :::為列表追加單個元素
    • ::::為列表追加另一個列表
    • ++:為列表追加另一個集合
    • ==:判斷集合類型和元素內容是否相同
    • distinct:返回不包含重復的列表
    • drop:去除列表前n個元素
    • filter:輸入一個謂詞函數,過濾掉指定的元素
    • flatten:將一個包含列表的列表轉換為元素列表
    • partition:輸入一個謂詞函數,將元素分組,由兩個列表組成一個元組
    • reverse:逆轉列表
    • slice:切片,輸入切片的開頭和結尾,輸出不包括結尾
    • sortBy:根據指定函數排序
    • sorted:根據自然順序排序
    • splitAt:根據指定索引將列表切分成兩個
    • take:從列表抽取前n個元素
    • zip:兩個列表合并為一個元組列表
  • 映射列表
    • collect:使用偏函數轉換各個元素,保留可應用的元素
    • flatMap:給定一個函數轉換各個元素,扁平化結果(消除列表中的列表,這種嵌套結構)
    • map:使用給定函數轉換各個元素
    • Scala可以把Java數組轉換為它自己的類型Array
  • 歸約列表
    • 將列表收縮為單個值
    • Scala的集合支持:
      • 數學歸約:maxminproductsum
      • 邏輯歸約:containsendsWithexistsforallstartsWith
      • 通用的高階操作,折疊,可以用來創建任何其他類型的列表歸約算法
        • foldfoldLeftfoldRightreducereduceLeftreduceRightscanscanLeftscanRight
  • 轉換集合
    • mkString:根據指定分隔符將一個集合轉換為String
    • toBuffer:將一個不可變的集合轉變為可變的集合
    • toList:將一個集合轉變為List
    • toMap:將一個2元元組的集合轉換為一個Map
    • toSet:將一個集合轉換為一個Set
    • toString:將一個集合呈現為一個String,包括集合的類型
  • JavaScala集合兼容性
    • 默認情況下兩者集合類型是不兼容的
    • 添加JavaConvertersimport collection.JavaConverters._
    • 集合轉換:asJavaasScala
  • 使用集合的模式匹配
    • 匹配表達式、模式哨衛、值綁定
    • 模式匹配是Scala語言的一個核心特性

更多集合

  • 可變的集合類型 : 不可變的集合類型

    • collection.mutable.Buffer : collection.imutable.List
    • collection.mutable.Set : collection.imutable.Set
    • collection.mutable.Map : collection.imutable.Map
    • 使用toListtoSettoMap將可變集合類型轉換回不可變的集合類型
  • 從不可變集合創建可變集合

    • 不可變集合ListMapSet都可以使用toBuffer方法轉換為可變的collection.mutable.Buffer類型
  • 使用集合構建器

    • Builder:生成指定的集合類型,只支持追加操作
    • Set.newBuilder[Char]
    • 需要調用result方法得到最終結果
  • 數組

    • Array是一個大小固定的可變索引集合,實際只是Java數組類型的一個包裝器,另外還提供了隱含類高級特性,使它可以項序列一樣使用
    • val numbers = Array(1, 2, 3, 4)
  • Seq和序列

    • Seq是所有序列的根類型

      序列集合層次體系

    • Seq:所有序列的根類型,List()的快捷方式

    • IndexedSeq:索引序列的根類型,Vector()的快捷方式

    • Vector:這一類列表有一個后備Array實例,可以按索引訪問

    • Range:整數范圍。動態生成數據

    • LinearSeq:線性(鏈表)序列的根類型

    • List:元素的單鏈表

    • Queue:先進先出列表

    • Stack:后進先出列表

    • Stream:懶列表。訪問元素時才增加相應元素

    • String:字符集合,擴展了Iterable

  • Stream

    • 懶集合,由一個或多個啟示元素和一個遞歸函數生成。流可能是無界的,理論上是無限的集合,只是在訪問元素時才回生成這個元素

    • 使用Stream.cons用表頭表尾構建一個新的流,替代語法,可以使用#::操作符

    • 示例:def inc(head: Int): Stream[Int] = head #:: inc(head+1)

    • 創建一個有界的流

      def to(head: Char, end: Char): Stream[Char] = (head > end) match {
          case true => Stream.empty
          case false => head #:: to((head+1).toChar, end)
      }
      
  • 一元集合

    • 支持類似Iterable中的變換操作,但是包含的元素不能多于1個
  • Option集合

    • 擴展了Iterable的一個一元集合,Option類型表示一個值的存在或不存在
    • Option可以安全地替代null值,告訴用戶這個值可能不存在,從而減少出發NullPointerException異常的可能性
    • 另一些開發人員把它看作是構建操作鏈的一種更為安全的方法,確保操作鏈中只包含有效的值
    • Option依賴兩個子類型提供的具體實現:SomeNone
      • Some:一個類型參數化的單元素集合
      • None:一個空集合。None類型沒有類型參數,因為它永遠不包含任何內容
      • isDefinedisEmpty分別檢查一個給定的OptionSome還是None
    • Option集合提供了一種安全的機制,而且還提供了一些操作來存儲和變換可能存在也可能不存在的值,還提供了一些安全操作可以抽取可能存在的值
    • 警告:避免使用Option.get(),此方法不安全,對None調用get()將導致運行時錯誤
    • 安全的Option抽取操作
      • fold
      • getOrElse:為Some,則返回值;為None,則返回傳名參數的結果
      • orElse:非空,返回這個Option,否則從給定的傳名參數返回一個Option
      • 匹配表達式 match{ case Some(x) => x; case None => -1 }
  • Try集合

    • 將錯誤處理轉變為集合管理。提供一種機制來捕獲給定函數參數中發生的錯誤,并返回這個錯誤。
    • Scala拋出一個異常會中斷程序流,并把控制交回給最近的處理器來處理這個特定的異常。未處理的異常會終止應用。
    • Scala支持try{}..catch{}塊。
    • 推薦使用util.Try(),因為它提供了一種更安全、更有表述性,而且純粹一元的方法來處理錯誤
    • util.Try類型沒有具體實現,但是有兩個已實現的子類型SuccessFailure
    • 使用Try的錯誤處理方法
      • flatMap:對于Success,調用一個同樣返回util.Try的函數,從而將當前返回值映射到一個新的內嵌返回值

      • foreach:一旦Success,執行給定的函數,Failure則什么都不做

      • getOrElse:返回Success中的內嵌值,對于Failure則返回傳名參數的值

      • orElse:與flatMap相反。對于Failure,調用一個同樣返回util.Try的函數

      • toOption:將util.Try轉換為Option,缺點是丟失內嵌的Exception

      • map:對于Success,調用一個函數,將內嵌值映射到一個新值

      • 匹配表達式 match { case util.Success(x) =>x; case util.Failure(error) => -1 }

      • 什么都不做:只需要允許異常在調用棧中向上傳播,直到被捕獲或者導致當前應用退出

        val input = " 123 "
        val result = util.Try(input.toInt) orElse util.Try(input.trim.toInt)
        result foreach { r => println(s"Parsed '$input' to $r!") }
        val x = result match {
            case util.Success(x) => Some(x)
            case util.Failure(ex) => {
                println(s"Couldn't parse input '$input'")
                None
            }
        }
        
  • Future集合

    • concurrent.Future,發起一個后臺任務
    • future表示一個可能的值,并提供了安全操作來串鏈其他操作或者抽取值。與OptionTry不同,future的值不是立即可用,因為創建future時后臺任務可能仍在工作。
    • 支持在并發線程中運行后臺任務。
    • 調用future并提供一個函數會在一個單獨的線程中執行該函數,而當前線程仍繼續操作
    • 在創建future之前,必須指定當前會話或應用的“上下文”來并發運行函數,默認使用global上下文
      • import concurrent.ExecutionContext.Implicits.global
    • 可以設置回調函數或另外的future,當future任務執行完成時,執行這個回調或者future
    • 異步處理future
      • fallbackTo:將第二個future串鏈到第一個future,如果第一個future不成功,則調用第二個future
      • flatMap:如果第一個成功,則使用第一個的返回值調用第二個future
      • map:將給定的函數串鏈到future,如果future成功,則其返回值將用來調用這個函數
      • onCompletefuture任務完成后,將用一個util.Try調用指定函數,其中包含一個值(成功)或者一個異常(失敗)
      • onFailure:如果future任務拋出一個異常,將使用這個異常來調用指定函數
      • onSuccess:如果future任務成功完成,將使用返回值來調用給定函數
      • Future.sequence:并發地運行給定序列中的future,返回一個新的future
    • 同步處理future
      • concurrent.Await.result(),取后臺線程和一個最大等待時間
      • 等待時間:import concurrent.duration._
      • 等待10s:val maxTime = Duration(10, SECONDS)

  • 基礎

    • Scala中,類參數在類名后指定,就像函數定義中函數參數跟在函數名后面一樣。類參數可以用來初始化字段(類中的值和變量),或者用于傳入函數,但是一旦類已經創建,這些參數就不再可用
    • 可以把某個字段聲明為類參數,通過在類參數前增加val或者var,類參數就成為類中的一個字段
    • Scala中,一個類可以使用extends關鍵字擴展最多一個其他類,另外可以用override關鍵字覆蓋所繼承方法的行為
    • 類中的字段和方法用this關鍵字訪問,父類中的字段和方法可以用super關鍵字訪問
    • Scala多態指類能夠采用其他兼容類的形式。“兼容”指一個子類的實例可以用于替代其父類的實例,但是反過來不行
  • 定義類

    class <identifier>  [type-parameters]
                        [([val|var] <identifier>: <type>[, ...])] //輸入參數可以有默認值
                        [extends <identifier>[type-parameters](<input parameters>)] //繼承父類可以有輸入參數
                        [{ fields, methods, and classes }]
    
    • 實例就是一個內存分配,提供了類字段的存儲空間。這個動作(即預留空間來分配一個類的內容)稱為實例化
    • 訪問類的字段和方法:標準中綴點記法、中綴操作符記法
  • 抽象類

    • 將由其他類擴展的一個類,而自己不能實例化
    • abstract關鍵字指定
    • 定義其子類必須有的核心字段和方法,但一般不提供具體實現(可以有默認實現)
    • 基于多態,如果一個值的類型為抽象類,它可以具體指向某個非抽象子類的實例,調用方法時實際上最后會在子類上調用
  • 重載方法

  • apply方法

    • 名為“apply”的方法有時是指它要作為一個默認方法或一個注入方法,可以直接調用而不需要方法名
    • 實際是一種快捷方式,可以使用小括號觸發功能而不需要方法名,就如python__call__方法,以及pytorchforward
  • 懶值

    • 定義一個值時可以在val關鍵字前面加上關鍵字lazy來創建一個懶值
    • 類中使用的字段(值和變量)都是在類第一次實例化時創建的,而懶值是第一次調用時創建
    • 要確保時間或者性能敏感操作在類的生命周期中只執行一次,懶值則是一種很好的方法
    • 常用于存儲基于文件的屬性、打開的數據庫鏈接,以及其他只有在確實必要時才初始化的不可變數據等信息。
  • 包裝

    • Scala文件定義包:package <identifier>
    • 源文件要存儲在與包匹配的目錄中
    • 訪問包裝類:用點分隔的包路徑或者導入包
    • Java不同,代碼中的任何位置都可以使用import,與python類似
    • 支持使用_操作符導入一個包的全部內容
    • 導入組:import <package>.{<class 1>[,<class 2>...]}
    • 使用導入別名:import <package>.{<original name> => <alias>}
    • 包裝語法:包作為一個塊,用大括號包圍它的類 package <identifier> { <class definitions> }
  • 私密性控制

    • 默認地,Scala不會增加私密性控制
    • protected:同一個類或者子類中的代碼才能訪問
    • private:僅定義這個字段或方法的類可以訪問
  • 私密性訪問修飾符

    • 包級保護
      • 另外,可以根據另一個類的親密性覆蓋這些策略
    • 實例級保護
  • 最終類和密封類

    • final類不能在子類中被覆蓋,final關鍵字定義的值,變量或方法,可以確保所有子類都將使用這個實現
    • sealed密封類
      • 密封類會限制一個類的子類必須位于福勒所在的同一個文件中

對象、Case類和Trait

  • 對象object
    • 一種類類型,只能有不超過1個實例,也就是單例(singleton
    • Java和其他語言的某些字段和方法為“靜態”或“全局”,對象可以提供類似的功能,不過將它們與可實例化的類解耦合
    • 對象不是用new關鍵字創建,只需要按名字直接訪問,在首次訪問時自動實例化(第一次訪問前,不會實例化)
    • 定義對象用object關鍵字,沒有任何輸入參數,對象不能擴展,但是別的類可以繼承它
    • 定義:object <identifier> [extends <identifier>] [{ fields, methods, and classes }]
    • 最適合object的函數是純函數和處理外部I/O的函數
      • 純函數:會返回完全由其輸入計算得到的結果,而沒有任何副作用
      • I/O函數:處理外部數據的函數,如處理文件、數據庫和外部服務
    • apply方法和伴生對象
      • 工廠模式是一種很流行的方法,可以從伴生對象生成一個類的新實例。

      • 伴生對象是與類同名的一個對象,與類在同一個文件中定義

        class Multiplier(val x: Int) { def product(y: Int) = x * y }
        object Multiplier { def apply(x: Int) = new Multiplier(x) }
        
    • 使用對象的命令行應用
      • 使用對象中的一個main方法作為應用的入口點
      • def main(args: Array[String]) { ... }
    • Case
      case class <identifier> ([var] <identifier>: <type>[,...])
                               [extends <identifier>(<input-parameters>)]
                               [{ fields and methods }]
      
      • 不可實例化的類,包含多個自動生成的方法,包括一個自動生成的伴生對象
      • 適合數據傳輸對象,主要用于存儲數據
      • val關鍵字可以用,但是case類默認將輸入參數轉換為值字段,所以沒必要添加,如果需要一個變量字段,則使用var關鍵字
      • case類方法
        • apply:對象object中,一個工廠方法,用于實例化case
        • copy:類中,返回實例的一個副本
        • equals:類中
        • hashCode:類中
        • toString:類中
        • unapply:對象中,將實例抽取到一個字段元組,從而可以使用case類實例完成模式匹配
    • Trait
      • trait不能實例化,支持多重繼承的類
      • trait不能有類參數,但可以有類型參數
      • 定義:trait <identifier> [extends <identifier>] [{ fields, methods, and classes }]
      • 用with關鍵字為類增加一個或多個trait,類沒有擴展trait,而是被trait擴展,trait為現有的類增加新的功能或者配置,這個特性也稱為依賴注入
        • Spring通過定制Java注解和初始化模塊實現了類似的功能,但是Scalatrait不要求特定的注解或特殊的包來實現依賴注入
    • 導入實例成員
      • 與導入類和對象的import使用方法一樣,可以導入單個成員,或者通過通配符_導入所有成員
      • 不會覆蓋私密性控制,只能導入可以正常訪問的字段和方法

高級類型

  • 暫略
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,488評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,034評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,327評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,554評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,337評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,883評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,975評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,114評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,625評論 1 332
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,555評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,737評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,244評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,973評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,615評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,343評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,699評論 2 370