天天看點

Scala的sealed關鍵字

Scala的sealed關鍵字

緣起

今天在學習​

​Akka​

​的監控策咯過程中看到了下面一段代碼:

def supervisorStrategy(): SupervisorStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 10 seconds) {
    case _: ArithmeticException => Resume
    case _: IllegalArgumentException => Restart
    case _: NullPointerException => Stop
    case _: Exception => Escalate
  }
      

當時有點好奇,就想去看看​

​Resume​

​, ​

​Restart​

​等的實作,于是就看到了下面的代碼:

object SupervisorStrategy extends SupervisorStrategyLowPriorityImplicits {

sealed trait Directive

/**
 * Resumes message processing for the failed Actor
 */
case object Resume extends Directive

/**
 * Discards the old Actor instance and replaces it with a new,
 * then resumes message processing.
 */
case object Restart extends Directive

/**
 * Stops the Actor
 */
case object Stop extends Directive

/**
 * Escalates the failure to the supervisor of the supervisor,
 * by rethrowing the cause of the failure.
 */
case object Escalate extends Directive

  // ....
}
      

剛剛Scala不久,不太清楚這裡的​

​sealed​

​關鍵字的作用,本文下面就詳細的描述一下這個關鍵字的作用吧。

模式比對

模式比對pattern matching在scala裡面是一個重量級的功能,依賴于模式比對可以優雅地實作很多功能。大緻格式如下:

selector match {
  pattern1 => <body1>
  pattern2 => <body2>
  ...
}
      

pattern總結起來大約以下幾類:

  • Wildcard patterns // _ 統配
  • Constant patterns // 常量
  • Variable patterns // 變量
  • Constructor patterns // 構造函數
  • Sequence patterns // 比如List(,). 如果需要比對剩餘的話使用List(0,_*)
  • Tuple patterns // (a,b,c)
  • Typed patterns // 使用類型比對 case a:Map[,]
  • asInstanceOf[]
  • isInstanceOf[]
  • note(dirlt):這裡需要注意容器類型擦除.Array例外因為這個是java内置類型

實際上我們還能夠使用pattern完成下面事情:

  • Patterns in variable definitions // val (a,b) = ("123","345");
  • Case sequences as partial functions
  • 直接使用pattern來構造函數.以參數為match對象,在body裡面直接編寫case.
  • Each case is an entry point to the function, and the parameters are specified with the pattern. The body of each entry point is the right-hand side of the case.
  • Patterns in for expressions // for ((country, city) <- capitals)

    // case sequences as partial function. val foo : Option[String] => String = { case Some(e) => e case None => "???" } val a = Option[String]("hello") println(foo(a)) val b = None println(foo(b))

pattern matching過程中還有下面幾個問題需要注意:

  • Patterns are tried in the order in which they are written.
  • Variable binding // 有時候我們希望比對的變量包含外層結構
  • A(1,B(x)) => handle(B(x))
  • A(1, p @ B(_)) => handle(p) # p綁定了B(x)這個比對
  • A(1, p @ B()) => handle(p) # B是可以包含unapply從type(p) => Boolean的類,做條件判斷
  • Pattern guards // 有時候我們希望對pattern做一些限制性條件
  • A(1,e,e) 比如希望後面兩個元素相等,但是這個在pm裡面沒有辦法表達
  • A(1,x,y) if x == y => // 通過guard來完成

scala為了友善擴充模式比對對象的case, 提供​

​case class​

​這個概念。case class和普通class大緻相同,不過有以下三個差別,定義上隻需要在class之前加上case即可:

  • 提供factory method來友善構造object
  • class parameter隐含val prefix
  • 自帶toString,hashCode,equals實作

    case class A(x:Int) {} // implicit val x:Int val a = A(1); // factory method. println(a.x); println(a); // toString = A(1)

case class最大就是可以很友善地用來做pattern matching.

如果我們能夠知道某個selector所有可能的pattern的話,那麼就能夠在編譯期做一些安全性檢查。但是selector這個過于寬泛,如果将selector限制在類層次上的話,那麼還是可以實作的。舉例如下:

abstract class A; // sealed abstract class A
case class B(a:Int) extends A;
case class C(a:Int) extends A;
case class D(a:Int) extends A;

val a:A = B(1);

a match {
  case e @ B(_) => println(e)
  case e @ C(_) => println(e)
}
      

在match a這個過程中,實際上我們可能存在B,C,D三種子類,但是因為我們這裡缺少檢查。使用sealed關鍵字可以完成這個工作。sealed class必須和subclass在同一個檔案内。A sealed class cannot have any new subclasses added except the ones in the same file. 如果上面增加sealed的話,那麼編譯會出現如下警告,說明我們沒有枚舉所有可能的情況。

/Users/dirlt/scala/Hello.scala:8: warning: match may not be exhaustive.
It would fail on the following input: D(_)
a match {
^
one warning found
      
(a : @unchecked)  match {
      case e @ B(_) => println(e)
      case e @ C(_) => println(e)
    }

    a match {
      case e @ B(_) => println(e)
      case e @ C(_) => println(e)
      case _ => throw new RuntimeException("??");
    }
      

sealed

  • 其修飾的trait,class隻能在目前檔案裡面被繼承
  • 用sealed修飾這樣做的目的是告訴scala編譯器在檢查模式比對的時候,讓scala知道這些case的所有情況,scala就能夠在編譯的時候進行檢查,看你寫的代碼是否有沒有漏掉什麼沒case到,減少程式設計的錯誤。