20.7. Ograniczenia kontekstu

Istnieje inna — obok ograniczenia widoku — skrócona notacja mająca związek z parametrami typu i listą parametrów niejawnych. Została ona zastosowana w przykładzie z pliku ContextBounds1.scala.

Plik ContextBounds1.scala:
trait Show[T] { def show(x: T):String }
object Show {
  implicit object ShowDouble extends Show[Double] {
    def show(x:Double) = f"$x%.2f"
  }
  implicit object ShowSymbol extends Show[Symbol] {
    def show(x:Symbol) = ":" + x.name
  }
}
object ShowUtil {
  def print1[T](value: T)(implicit s: Show[T]) = print(s.show(value))
  def print2[T : Show](value: T) =
    print(implicitly[Show[T]].show(value))
}

Cecha Show definiuje metodę show, która zwraca wartość tekstową swojego argumentu. Obiekt towarzyszący tej cechy zawiera definicje dwóch obiektów rozszerzających typy Show[Double] oraz Show[Symbol] i zdefiniowanych z modyfikatorem implicit. Metoda print1 ma parametr s typu Show[T] na liście parametrów niejawnych. Można przyjąć, że istnienie parametru s w jej definicji nakłada na parametr T tej metody dodatkowy warunek dotyczący kontekstu, w jakim metoda print1 może być wywołana. W tym kontekście musi mianowicie być dostępny odpowiedni obiekt typu Show[T], zdefiniowany z modyfikatorem implicit. Język Scala pozwala zapisać taki warunek za pomocą tak zwanego ograniczenia kontekstu, użytego w definicji metody print2. Ograniczenie kontekstu B parametru typu A jest oznaczane jako A : B i oznacza, że parametrowi A może odpowiadać typ, dla którego w momencie wywołania metody może być skutecznie (w sposób jednoznaczny) znaleziony obiekt oznaczony modyfikatorem implicit pasujący do typu B[A]. Metoda zawierająca ograniczenie kontekstu A : B jest odpowiednikiem metody, która ma parametr A bez tego ograniczenia, ale za to ma na dodatkowej liście parametrów niejawnych parametr typu B[A]. Taka relacja zachodzi między metodami print1 i print2. Obie metody działają analogicznie. Użyta w definicji metody print2 metoda implicitly pozwala odnaleźć wartość określonego typu zdefiniowaną z modyfikatorem implicit. Z powodu zastosowania ograniczenia kontekstu, w definicji metody print2 nie ma jawnie zdefiniowanej listy parametrów niejawnych i w związku z tym nie ma również nazwy parametru. Użycie metody implicitly rozwiązuje ten problem.

scala> ShowUtil.print1(2.0)
2,00
scala> ShowUtil.print1('abc)
:abc
scala> ShowUtil.print2(2.0)
2,00
scala> ShowUtil.print2('abc)
:abc

W obiekcie ShowUtil zdefiniowane są obiekty dziedziczące typy Show[Double] i Show[Symbol], ale nie ma obiektu typu Show[Int]. Z tego powodu poniższe próby wywołania metod print1 i print2, w której metodom tym przekazywane są wartości typu Int, nie kompilują się.

scala> ShowUtil.print1(2)
<console>:11: error: could not find implicit value for parameter s: Show[Int]
       ShowUtil.print1(2)
                      ^

scala> ShowUtil.print2(2)
<console>:11: error: could not find implicit value for evidence parameter of type Show[Int]
       ShowUtil.print2(2)
                      ^

Ogranicznie kontekstu może być również zastosowane w przypadku parametrów typu w klasach (ale nie w cechach, które nie mają parametrów konstruktora i w związku z tym nie mogą mieć parametrów niejawnych). Plik ContextBounds2.scala przedstawia definicję klasy ShowUtil2, która ma parametr T z ograniczeniem kontekstu.

Plik ContextBounds2.scala:
class ShowUtil2[T : Show](value: T) {
  def print2 = print(implicitly[Show[T]].show(value))
}

Poniższe polecenia ilustrują użycie tej klasy.

scala> (new ShowUtil2(2.0)).print2
2,00
scala> (new ShowUtil2('abc)).print2
:abc

Jeśli parametr typu ma jednocześnie ograniczenie górne, dolne oraz ograniczenie widoku i kontekstu, to należy je umieścić w odpowiedniej kolejności: najpierw ograniczenie dolne, potem górne, potem ograniczenie widoku, a na końcu kontekstu. Zdefiniowana poniżej (nic nie robiąca) metoda dummy ma wszystkie cztery rodzaje ograniczeń zadeklarowane we właściwej kolejności.

scala> def dummy[A >: Nothing <: Any <% AnyRef : Option]() {}
dummy: [A]()(implicit evidence$1: A => AnyRef, implicit evidence$2: Option[A])Unit

Specyfikacja języka Scala opisuje ograniczenia kontekstu w punkcie 7.4.

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.