1. 概要

この簡単な記事では、抽出オブジェクトと、Scalaパターンマッチングでのそれらのユースケースを紹介します。

Scalaでは、 unapply()メソッドを持つすべてのオブジェクトはエクストラクタオブジェクトと呼ばれます。 unapply()メソッドは、オブジェクトに圧縮されたデータ値を抽出するのに役立つため、Scalaのパターンマッチングの基礎です。

2. apply()およびファクトリメソッド

unapply()メソッドは apply()メソッドの反対であるため、エクストラクターオブジェクトを導入する前に、 apply()メソッドについて知っておく必要があります。

デフォルトのコンストラクターを持つUserクラスについて考えてみます。

class User(val name: String, val age: Int)

User のインスタンスを作成する場合は常に、newキーワードを使用してコンストラクターを呼び出す必要があります。

val user = new User("John", 25)

apply()メソッドをユーザーのコンパニオンオブジェクトのファクトリメソッドとして使用できます。

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

ここで、 apply()メソッドを呼び出すことにより、Userクラスをインスタンス化できます。

val user = User.apply("John", 25)

きちんとしたコードの場合、 apply()メソッドなしでクラス名を呼び出すだけで、 apply()シンタックスシュガーバージョンを使用します。

val user = User("John", 25)

また、 Userクラスに対して複数のapply()メソッドを記述できます。

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

したがって、 User( “Jack”)、を呼び出すだけで、年齢がゼロのUserをインスタンス化できます。

3. unapply()およびExtractorオブジェクト

unapply()メソッドは、apply()メソッドの正反対です。 このメソッドは、すべてのオブジェクト値を抽出し、結果としてそれらを一覧表示します。 ベストプラクティスは、 unapply() クラスのコンパニオンオブジェクトのメソッド:

object User {
  def unapply(u: User): Option[(String, Int)] = Some(u.name, u.age)
}

これで、 unapply()を明示的に呼び出すことができます。

scala> User.unapply(user)
res8: Option[(String, Int)] = Some((jack,30))

これは、Scalaのパターンマッチングがオブジェクト値を抽出する方法です。 unapply()メソッドがないと、パターンマッチングを実行できません。

user match {
  case User(_, age) if age < 18 => 
    println("You are not allowed to get a driver license.")
  case User(_, age) if age >= 18 =>
    println("You are allowed to get a driver's license.")
}

カスタマイズされたunapply()メソッドを記述して、オブジェクトからより多くの詳細を抽出できることに注意してください。 「2019-05-28」のような日時文字列から日、月、年を抽出する抽出オブジェクトを考えてみてください。

4.  unapplySeq()および分解シーケンス

unapply()メソッドは、ユーザーから名前年齢などのさまざまな単一値アイテムを抽出する場合に役立ちます。 。 一方、インスタンスを値のコレクションに分解する場合は、unapplySeq()メソッドを使用する必要があります。

たとえば、HttpRequestインスタンスをそのHeaderに分解するとします。

case class Header(key: String, value: String)
class HttpRequest(val method: String, val uri: String, val headers: List[Header])

コンパニオンオブジェクトでunapplySeq()メソッドを宣言する場合:

object HttpRequest {
  def unapplySeq(request: HttpRequest): Option[List[Header]] = Some(request.headers)
}

次に、リクエストを構成ヘッダーに分解し、そのヘッダーのコレクションにパターンマッチングを適用できます。

val headers = Header("Content-Type", "application/json") ::
  Header("Authorization", "token") :: Header("Content-Language", "fa_IR") :: Nil
val request = new HttpRequest("GET", "localhost", headers)
request match {
  case HttpRequest(h1) => println(s"The only header is $h1")
  case HttpRequest(h1, h2) => println(s"We have two headers: $h1 and $h2")
  case HttpRequest(all @ _*) => print(s"All headers are as following: $all")
}

リクエストにそれぞれ1つまたは2つのヘッダーが含まれている場合、最初のケースと2番目のケースが一致します。 後者の場合、繰り返しの数のヘッダー( _ *”部分)を変数allにバインドします。 明らかに、後者の場合は指定された要求に一致するため、このプログラムは次のように出力します。

All headers are as following: List(Header(Content-Type,application/json), 
  Header(Authorization,token), Header(Content-Language,fa_IR))

unapplySeq()メソッドを unapply()に置き換えると、同じパターンが次のメッセージで失敗します。

too many patterns for object HttpRequest offering List[Header]: expected 1, found 2

簡単に言うと、 unapply()を使用する場合、List [Header]全体が単一の値と見なされますが、unapplySeq()を使用すると、シーケンスの各メンバーに対してパターンマッチングを行うことができます。

5. 結論

apply()は、データ型とクラスのファクトリメソッドを作成する方法ですが、 unapply()は、クラスを値に分解するのに適しています。 apply()とunapply()を特別なものにしているのは、オブジェクトをサイレントに作成および分解することです

User.apply( “Jack”、30)の代わりに User( “Jack”、30)を呼び出すだけで、 apply()メソッドを使用します。 また、 user オブジェクトのパターンマッチングにより、 unapply()メソッドがサイレントに呼び出されます。

いつものように、すべてのコード実装はGitHub利用できます。