Są to tak zwane ograniczenia typu uogólnionego . Pozwalają one, w obrębie klasy lub cechy sparametryzowanej na typ, na dalsze ograniczenie jednego z jego parametrów typu. Oto przykład:
case class Foo[A](a:A) { // 'A' can be substituted with any type
// getStringLength can only be used if this is a Foo[String]
def getStringLength(implicit evidence: A =:= String) = a.length
}
Argument niejawny evidence
jest dostarczany przez kompilator, iff A
jest String
. Można myśleć o nim jako dowód , że A
jest String
sama --the argumentem nie jest ważne, tylko wiedząc, że ona istnieje. [edytuj: cóż, technicznie jest to faktycznie ważne, ponieważ reprezentuje niejawną konwersję z A
na String
, co pozwala ci dzwonić, a.length
a kompilator nie krzyczy na ciebie]
Teraz mogę go używać w następujący sposób:
scala> Foo("blah").getStringLength
res6: Int = 4
Ale jeśli spróbuję użyć tego z Foo
zawartością czegoś innego niż String
:
scala> Foo(123).getStringLength
<console>:9: error: could not find implicit value for parameter evidence: =:=[Int,String]
Możesz odczytać ten błąd jako „nie można znaleźć dowodów na to, że Int == String” ... tak powinno być! getStringLength
nakłada dalsze ograniczenia na rodzaj, A
niż Foo
ogólnie wymaga; mianowicie, możesz wywoływać tylko getStringLength
na Foo[String]
. To ograniczenie jest wymuszane w czasie kompilacji, co jest fajne!
<:<
i <%<
działają podobnie, ale z niewielkimi zmianami:
A =:= B
oznacza, że A musi być dokładnie B
A <:< B
oznacza, że A musi być podtypem B (analogicznie do ograniczenia typu prostego<:
)
A <%< B
oznacza, że A musi być widoczne jako B, prawdopodobnie poprzez niejawną konwersję (analogicznie do prostego ograniczenia typu <%
)
Ten fragment kodu @retronym jest dobrym wytłumaczeniem tego, jak kiedyś tego rodzaju rzeczy były osiągane i jak ogólne ograniczenia typów ułatwiają to teraz.
UZUPEŁNIENIE
Aby odpowiedzieć na twoje pytanie uzupełniające, co prawda podany przeze mnie przykład jest dość przemyślany i nie jest oczywiście użyteczny. Ale wyobraź sobie, że używasz go do zdefiniowania czegoś takiego jak List.sumInts
metoda, która tworzy listę liczb całkowitych. Nie chcesz zezwalać na wywoływanie tej metody na żadnym starym List
, tylko List[Int]
. Jednak List
konstruktor typu nie może być tak constrainted; nadal chcesz mieć listę ciągów znaków, foos, barów i innych rzeczy. Tak więc, umieszczając uogólnione ograniczenie typu sumInts
, możesz upewnić się, że tylko ta metoda ma dodatkowe ograniczenie, którego można użyć tylko w przypadku List[Int]
. Zasadniczo piszesz specjalny kod dla niektórych rodzajów list.