Scala 提取器
Scala 提取器
Section titled “Scala 提取器”提取器是 Scala 中定义了 unapply 方法的对象。提取器的主要目的是解构(deconstruct)一个值,通常在模式匹配(case 子句)中使用,有效地反转了构造过程。
虽然提取器通常与 apply 方法(用于构造)配对出现,但对象要成为提取器只需要 unapply 方法。
unapply 方法签名:
Section titled “unapply 方法签名:”unapply 方法将待解构的值作为输入,通常返回一个包含提取组件的 Option。
- 用于单个值提取:
def unapply(input: InputType): Option[ExtractedType] - 用于多个值提取:
def unapply(input: InputType): Option[(Type1, Type2, ...)](返回一个包含 Tuple 的 Option)
Option 返回类型处理输入不匹配预期结构的情况:Some(...) 表示成功匹配和提取,而 None 表示失败。
示例:电子邮件提取器
Section titled “示例:电子邮件提取器”让我们创建一个提取器对象来解析电子邮件地址为用户和域名部分。
object Email { // 可选:用于构造的 'apply' 方法(注入) def apply(user: String, domain: String): String = { s"$user@$domain" }
// 必须:用于解构的 'unapply' 方法(提取) def unapply(emailString: String): Option[(String, String)] = { val parts = emailString.split('@') if (parts.length == 2 && parts(0).nonEmpty && parts(1).nonEmpty) { Some((parts(0), parts(1))) // 成功:返回 Some(tuple) } else { None // 失败:返回 None } }}
@main def ExtractorDemo(): Unit = { // 使用 apply (可选) val emailAddr = Email("alice", "example.com") println(s"Constructed: $emailAddr")
// 通过模式匹配隐式使用 unapply def checkEmail(addr: String): Unit = addr match { case Email(user, domain) => // unapply 在这里被调用! println(s"Matched! User: $user, Domain: $domain") case _ => println(s"'$addr' is not a valid email address.") }
checkEmail("bob@test.org") checkEmail("invalid-email") checkEmail(emailAddr) // 检查我们构造的那个}输出:
Constructed: alice@example.comMatched! User: bob, Domain: test.org'invalid-email' is not a valid email address.Matched! User: alice, Domain: example.com在 match 表达式中,case Email(user, domain) => ... 触发 Email.unapply(addr) 调用。如果它返回 Some((u, d)),则匹配成功,并且 u 被绑定到 user,d 被绑定到 domain。如果它返回 None,则该 case 失败。
Case Class 与提取器:
Section titled “Case Class 与提取器:”case class 的一个关键特性是 Scala 编译器会根据类参数自动生成一个包含 apply 和 unapply 方法的伴生对象。这使得它们可以开箱即用地与模式匹配无缝协作。
case class Person(name: String, age: Int)
val alice = Person("Alice", 30)
alice match { // 调用编译器自动生成的 Person.unapply(alice) case Person(n, a) => println(s"Name: $n, Age: $a")}用于布尔检查的提取器:
Section titled “用于布尔检查的提取器:”unapply 方法也可以返回 Boolean 用于简单的检查(尽管 Option[Boolean] 或自定义类型通常更清晰)。如果 unapply(value) 返回 true,则模式匹配。
object IsEven { def unapply(num: Int): Boolean = num % 2 == 0}
@main def BooleanExtractorDemo(): Unit = { val number = 42 number match { case IsEven() => println(s"$number is even.") // 如果 IsEven.unapply(number) 为 true 则匹配 case _ => println(s"$number is odd.") }
val oddNum = 7 oddNum match { case IsEven() => println(s"$oddNum is even.") case _ => println(s"$oddNum is odd.") // 在这里匹配 }}可变参数提取器 (unapplySeq):
Section titled “可变参数提取器 (unapplySeq):”对于匹配元素序列,提取器可以定义 unapplySeq 方法。它返回一个序列类型(如 Seq[T])的 Option。这使得 case MyExtractor(a, b, _*) => ... 这样的模式成为可能。
object DomainParts { // 提取由点分隔的域名部分 def unapplySeq(domain: String): Option[Seq[String]] = { val parts = domain.split('.').toSeq if (parts.nonEmpty && parts.forall(_.nonEmpty)) Some(parts) else None }}
@main def UnapplySeqDemo(): Unit = { "www.example.com" match { // 如果存在 3 个部分则匹配,忽略其余部分 case DomainParts(first, second, third, _*) => println(s"Major parts: $first, $second, $third") // www, example, com case _ => println("Could not parse domain") }
"example.co.uk" match { case DomainParts(name, tld1, tld2) => // 精确匹配 3 个部分 println(s"Name: $name, TLDs: $tld1.$tld2") // example, co.uk case _ => println("Not a 3-part domain") }}提取器提供了一种强大的机制,用于自定义模式匹配,实现对复杂数据类型的干净解构。