20.6. Ograniczenia widoku
Parametr zadeklarowany na liście parametrów implicit może definiować niejawną konwersję jednego typu na drugi. Taka sytuacja ma miejsce w definicji metody print1 z pliku ViewBounds1.scala.
Plik ViewBounds1.scala: trait Show { def show:String } object Show { implicit class ShowDouble(x: Double) extends Show { def show = f"$x%.2f" } implicit class ShowSymbol(x: Symbol) extends Show { def show = ":" + x.name } } object ShowUtil { def print1[T](value: T)(implicit s: T => Show) = print(value.show) def print2[T <% Show](value: T) = print(value.show) }
Przeanalizujmy dokładniej cały przykład. Cecha Show definiuje metodę show, która zwraca wartość tekstową. Obiekt towarzyszący tej cechy zawiera definicje dwóch klas definiujących niejawne konwersje z typów Double i Symbol na typ Show. Metoda print1 ma na liście parametrów niejawnych parametr, który ma typ odpowiadający niejawnej konwersji z typu T na typ Show. W ciele metody znajduje się wywołanie metody show na obiekcie value typu T. Dzięki niejawnej konwersji reprezentowanej przez parametr s, to wywołanie kompiluje się, mimo że typ T nie musi zawierać metody show. Zauważmy, że taka definicja metody print1 nakłada dodatkowe ograniczenie na typ parametru T — mianowicie takie, że musi być dostępna niejawna konwersja z typu T na typ Show. Definicja metody print2 korzysta z innego sposobu zapisu analogicznego ograniczenia dotyczącego parametru typu T. Ten skrócony sposób zapisu wykorzystuje tak zwane ograniczenie widoku. Ograniczenie widoku B parametru typu A oznaczane jest jako A <% B i oznacza, że parametrowi A może odpowiadać dowolny typ, który może być przekształcony na typ B za pomocą niejawnej konwersji, czyli innymi słowy parametrowi A może odpowiadać typ, dla którego dostępny jest widok przekształcający go na typ B. Metoda zawierająca ograniczenie widoku A <% B jest odpowiednikiem metody, która ma parametr A bez tego ograniczenia, ale za to ma na liście parametrów niejawnych dodatkowy parametr definiujący konwersję z A na B. Taka relacja zachodzi między metodami print1 i print2. Obie metody działają analogicznie.
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ą niejawne konwersje z typów Double i Symbol na Show, ale nie ma niejawnej konwersji z typu Int na Show. 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: No implicit view available from Int => Show. ShowUtil.print1(2) ^ scala> ShowUtil.print2(2) <console>:11: error: No implicit view available from Int => Show. ShowUtil.print2(2) ^
Ogranicznie widoku 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 ViewBounds2.scala przedstawia definicję klasy ShowUtils2, która ma parametr T z ograniczeniem widoku.
Plik ViewBounds2.scala: class ShowUtil2[T <% Show](value: T) { def print2 = print(value.show) }
Poniższe polecenia ilustrują użycie tej klasy.
scala> (new ShowUtil2(2.0)).print2 2,00 scala> (new ShowUtil2('abc)).print2 :abc
Specyfikacja języka Scala opisuje ograniczenia widoku w punkcie 7.4. |