15.4. Metody apply, unapply i unapplySeq
Utworzenie klasy przypadku może spowodować dodanie do jej obiektu towarzyszącego (a jeśli nie był jawnie zdefiniowany, to także utworzenie go) metod apply oraz jednej z metod unapply lub unapplySeq.
Metoda apply ma listę parametrów odpowiadającą liście elementów klasy i pozwala tworzyć instancje klasy w sposób przypominający tworzenie tych instancji za pomocą operatora new, tyle że z pominięciem tego operatora.
scala> val e1 = E.apply(1,"e",3L) e2: E = E(1,e,3) scala> val e2 = E(1,"e",3L) e2: E = E(1,e,3)
Metoda apply nie jest tworzona w przypadku abstrakcyjnych klas przypadku.
scala> abstract case class F(a: Int)
defined class F
scala> F.apply(1)
<console>:13: error: value apply is not a member of object F
F.apply(1)
^
Metody unapply i unapplySeq służą do obsługi przez klasę przypadku dopasowywania wzorców i są generowane w sposób odzwierciedlający liczbę i typy elementów klasy. Dzięki temu można stosować wzorce o nazwach takich jak nazwy klas przypadku i o parametrach odpowiadających elementom klas.
Plik CaseClassF.scala: case class F0() case class F1(x: Int) case class F2(x: Int, y: Int) case class F3(x: String*) case class F4(x: Int, z: String*) def testF(x: Any):String = x match { case F0() => "F0" case F1(y) => s"F1($y)" case F2(2,_) => "F2(2,?)" case F3() => "F3()" case F3(a) => s"F3($a)" case F3(a,b) => s"F3($a,$b)" case F4(a) => s"F4($a)" case F4(a,b) => s"F4($a,$b)" case F4(a,b,_*) => s"F4($a,$b,...)" case _ => "" }
Plik CaseClassF.scala zawiera definicje kilku klas przypadku oraz definicję metody testF, w której użyte są wzorce dopasowujące instancje tych klas.
scala> :load CaseClassF.scala
Loading CaseClassF.scala...
defined class F0
defined class F1
defined class F2
defined class F3
defined class F4
testF: (x: Any)String
scala> testF(F0())
res2: String = F0
scala> testF(F1(1))
res3: String = F1(1)
scala> testF(F2(2,3))
res4: String = F2(2,?)
scala> testF(F3("a","b"))
res5: String = F3(a,b)
scala> testF(F4(5))
res6: String = F4(5)
scala> testF(F4(5,"a"))
res7: String = F4(5,a)
scala> testF(F4(5,"a","b"))
res8: String = F4(5,a,...)
Przykład z pliku CaseClassConflict.scala pokazuje konflikt między jawnie zdefiniowaną metodą, a metodą generowaną domyślnie w obiekcie towarzyszącym. Obiekt F5 definiuje metodę o nazwie apply i liście parametrów takiej, jak lista parametrów klasy F5.
Plik CaseClassConflict.scala: case class F5(a: Int) object F5 { def apply(a: Int) = () }
Próba kompilacji pliku CaseClassConflict.scala nie udaje się.
$ scalac CaseClassConflict.scala
CaseClassConflict.scala:1: error: method apply is defined twice
conflicting symbols both originated in file 'H:\jps2\examples\CaseClassConflict.scala'
case class F5(a: Int)
^
one error found
Plik CaseClassF.scala:
case class F0()
case class F1(x: Int)
case class F2(x: Int, y: Int)
case class F3(x: String*)
case class F4(x: Int, z: String*)
def testF(x: Any):String = x match {
case F0() => "F0"
case F1(y) => s"F1($y)"
case F2(2,_) => "F2(2,?)"
case F3() => "F3()"
case F3(a) => s"F3($a)"
case F3(a,b) => s"F3($a,$b)"
case F4(a) => s"F4($a)"
case F4(a,b) => s"F4($a,$b)"
case F4(a,b,_*) => s"F4($a,$b,...)"
case _ => ""
}
