14.5. Typy we wzorcach
Wzorzec może składać się ze zmiennej lub znaku podkreślenia, po których następuje dwukropek oraz wzorzec dotyczący typu. Taki wzorzec dopasowuje wartości, których typ odpowiada podanemu po prawej stronie dwukropka wzorcowi typu. W przypadku użycia zmiennej, a nie znaku podkreślenia, dopasowana wartość zostaje dodatkowo przypisana do zmiennej, a zmienna staje się zmienną dopasowanego typu. Wzorzec typu może przyjmować różne formy. Jedną z nich jest nazwa klasy. Takiego rodzaju wzorce typu są wykorzystane w metodzie typePattern1, zdefiniowanej w pliku PatternsTypes1.scala.
Plik PatternsTypes1.scala: def typePattern1(value: Any):String = value match { case _ :Int => "Int matched" case x :String => "String matched. "+x.toUpperCase
case _ :Boolean => "Boolean matched" case _ => "Another type matched" }
W drugim wzorcu (wiersz
) dopasowanie jest ograniczone do wartości
typu String, a dopasowana wartość jest przypisywana zmiennej
x. Zmienna x ma po prawej stronie strzałki typ String, co
umożliwia użycie metody toUpperCase, a co byłoby niemożliwe w
przypadku, gdyby x było typu Any.
scala> :load PatternsTypes1.scala Loading PatternsTypes1.scala... typePattern1: (value: Any)String scala> typePattern1(4) res0: String = Int matched
scala> typePattern1("abcd")
res1: String = String matched. ABCD
scala> typePattern1(2.0)
res2: String = Another type matched
scala> typePattern1(true)
res3: String = Boolean matched
scala> typePattern1('abc)
res4: String = Another type matched
scala> typePattern1(None)
res5: String = Another type matched
Metoda typePattern2, zdefiniowana w pliku PatternsTypes2.scala, zawiera przykłady innych postaci, jakie mogą przybierać wzorce typu.
Plik PatternsTypes2.scala: class A { class B } class D trait E def typePattern2(value: Any):String = value match { case _ :scala.Symbol => "Symbol matched"
case _ :A#B => "A#B matched"
case _ :None.type => "None.type matched"
case _ :D with E => "D with E matched"
case _ => "Not matched" }
Wzorzec z wiersza
zawiera odwołanie do pełnej nazwy klasy, wraz z
nazwą pakietu, w którym się ona znajduje. Wzorzec z wiersza
dopasowuje obiekty klasy B, zdefiniowanej w klasie A. Wzorzec z
wiersza
jest przykładem typu singletonowego. Wzorzec z wiersza
jest przykładem wzorca złożonego. Wzorzec złożony ma postać T1 with
T2 with … Tn, gdzie T1, T2 … Tn są wzorcami typu. Taki wzorzec
dopasowuje wartości, które mogą być dopasowane do każdego z wzorców
T1, T2 ... Tn.
scala> :load PatternsTypes2.scala
Loading PatternsTypes2.scala...
defined class A
defined class D
defined trait E
typePattern2: (value: Any)String
scala> typePattern2('abc)
res6: String = Symbol matched
scala> val a = new A
a: A = A@44120
scala> val b = new a.B
b: a.B = A$B@1f417a7
scala> typePattern2(b)
res7: String = A#B matched
scala> typePattern2(None)
res8: String = None.type matched
scala> typePattern2(new D)
res9: String = Not matched
scala> typePattern2(new E{})
res10: String = Not matched
scala> typePattern2(new D with E)
res11: String = D with E matched
Wzorce typu mogą zawierać zmienne typu lub znaki podkreślenia i dopasowywać typy posiadające parametry. Metoda typePattern3, z pliku PatternsTypes3.scala, zawiera przykłady takich wzorców.
Plik PatternsTypes3.scala: def typePattern3(value: Any):String = value match { case _ :Array[Array[Int]] => "Array[Array[Int]] matched"
case _ :Array[Array[_]] => "Array[Array[_]] matched"
case _ :Array[Int] => "Array[Int] matched"
case _ :Array[String] => "Array[String] matched"
case _ :Array[x] => "Array[x] matched"
case _ :Option[_] => "Option[_] matched"
case _ :List[_] => "List[_] matched"
case _ => "Not matched" }
Wzorzec z wiersza
dopasowuje tablice tablic wartości typu
Int. Wzorzec z wiersza
dopasowuje tablice tablic wartości innych
typów. W tym wzorcu, zamiast parametru typu, jest użyty znak
podkreślenia, oznaczający dowolny typ. Wzorce z wierszy
i
dopasowują tablice wartości typu Int i String. Wzorzec z wiersza
dopasowuje pozostałe tablice. W tym wzorcu, zamiast parametru typu
jest użyta zmienna, która dopasowuje dowolny typ. Wzorce z wierszy
i
dopasowują odpowiednio wartości typów ogólnych Option i List.
scala> :load PatternsTypes3.scala
Loading PatternsTypes3.scala...
typePattern3: (value: Any)String
scala> typePattern3(Array(Array(1),Array(2,3)))
res12: String = Array[Array[Int]] matched
scala> typePattern3(Array(Array("a"),Array("b","c")))
res13: String = Array[Array[_]] matched
scala> typePattern3(Array(1,2))
res14: String = Array[Int] matched
scala> typePattern3(Array("a","b"))
res15: String = Array[String] matched
scala> typePattern3(Array('a,'b))
res16: String = Array[x] matched
scala> typePattern3(List(1,2,3,4))
res17: String = List[_] matched
Nie jest przypadkiem to, że w niektórych wzorcach dopasowujących
tablice zastosowanych w metodzie typePattern3 zastosowane zostały
wzorce dopasowujące konkretne klasy (wzorce z wierszy
,
i
) w
miejscu parametrów typu, natomiast w przypadku wzorców zastosowanych
do dopasowania parametrów typów Option i List użyto jedynie znaku
podkreślenia. Na platformie JVM typy używanych tablic są dostępne w
czasie wykonywania programu, natomiast w przypadku innych typów
ogólnych informacje o parametrach typu nie są dostępne w czasie
wykonania programu, co jest związane z mechanizmem wymazywania
informacji o parametrach typów ogólnych.
W pliku PatternsTypes4.scala zdefiniowana jest metoda
typePattern4, w której wykorzystano, w wierszu
, wzorzec
dopasowujący obiekty typu Option, w którym, w miejscu parametru
typu, użyto nazwy konkretnej klasy (Int).
Plik PatternsTypes4.scala: def typePattern4(value: Any):String = value match { case _ :List[Int] => "List[Int] matched"
case _ :List[_] => "List[_] matched"
case _ => "Not matched" }
Kompilacja tej metody powoduje wystąpienie ostrzeżeń.
scala> :load PatternsTypes4.scala
Loading PatternsTypes4.scala...
<console>:12: warning: non-variable type argument Int in type pattern List[Int] (the underlying of List[Int]) is unchecked since it is eliminated by erasure
case _ :List[Int] => "List[Int] matched"
^
<console>:13: warning: unreachable code
case _ :List[_] => "List[_] matched"
^
typePattern4: (value: Any)String
Metoda kompiluje się i może być używana, ale ostrzeżenie zawiadamia o tym, że użyty został wzorzec nie będący zmienną w odniesieniu do parametru typu. Poniższe przykłady użycia metody typePattern4 pokazują problem związany z używaniem takiego wzorca.
scala> typePattern4(List(1,2))
res18: String = List[Int] matched
scala> typePattern4(List("a","b"))
res19: String = List[Int] matched
Obie listy zostały dopasowane przez pierwszy z wzorców (z wiersza
), mimo że pierwsza z list zawiera wartości typ
Int, a druga wartości typu String. Z powodu tego, że informacja o
parametrach typu jest wymazywana, pierwszy z wzorców (z wiersza
) dopasowuje listy zawierające wartości dowolnego
typu, a drugi (z wiersza
) jest niepotrzebny.
Plik PatternsTypes1.scala:
def typePattern1(value: Any):String =
value match {
case _ :Int => "Int matched"
case x :String => "String matched. "+x.toUpperCase 