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, T2Tn 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.

Język programowania Scala Wydanie 2. Copyright © Grzegorz Balcerek 2016

Licencja Creative Commons

Ten utwór jest dostępny na licencji Creative Commons Uznanie autorstwa-Na tych samych warunkach 4.0 Międzynarodowe.

Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners.