2013-09-01 13 views
5

Gram w kolekcje w Scali i stwierdziłem, że zbiory zmienne są definiowane jako niezmienne, a niezmienne kolekcje są definiowane jako kowariantne. Jaka jest zależność między zmiennością a zmiennością/niezmiennością w Scali?zależność między wariancją a zmiennością/niezmiennością w Scali

class Array[T] 

class List[+T] 
+0

To jest właśnie relacja. Niezmienna jest kowariantna, zmienna jest niezmienna. –

+0

Faktem jest, że zmienne struktury "wpadają w kłopoty", gdy pozwalasz im być kowariantnymi. Z tego powodu Scala ogranicza pozycje, w których można umieścić parametry typu kowariantnego. Zajrzyj tutaj: http://www.artima.com/pins1ed/type-parameterization.html Jeśli możesz, spróbuj znaleźć ten sam rozdział w 2 edycji książki. – Felix

Odpowiedz

4

Typ może być oznaczony jako kowariant tylko wtedy, gdy ten typ parametru pojawia się tylko w pozycjach kowariancyjnych. Ogólnie oznacza to, że klasa/cecha/obiekt ma metody, które zwracają wartości typu wariantu, ale nie mają metod z parametrami typu wariantu. Zbiory zmutowane zawsze mają metody z parametrami typu wariantu, np. update. Wyobraźmy sobie, co by się stało, gdyby Array mogłaby zostać uznana Array [+ T]:

val as = Array[String]("a string") 

// this statement won't typecheck in actual Scala 
val aa: Array[AnyRef] = as 

aa(0) = ("I'm a string...", "but this tuple itself isn't!") 

// Tuples don't have a substring method, so this would fail at run-time 
// if the compiler allowed this code to compile. 
as(0).substring(0) 
5

znalazłem łatwe wyjaśnienie w SIA. Dalej jest prosto.

Zmienne obiekty muszą być niezmienne. Parametr typu jest niezmienny, gdy nie jest kowariantny ani przeciwstawny. Wszystkie zmienne klasy kolekcji Scala są niezmienne. Przykład może wyjaśnić, dlaczego zmienne obiekty muszą być niezmienne. Ponieważ ListBuffer jest zmienny, to zadeklarowana jako niezmienne, co następuje:

final class ListBuffer[A] ...{ ... } 

ponieważ jest zadeklarowana jako niezmienny, nie można przypisać ListBuffer z jednego typu na inny. Poniższy kod wygeneruje błąd kompilacji:

scala> val mxs: ListBuffer[String] = ListBuffer("pants") 
mxs: scala.collection.mutable.ListBuffer[String] = 
      ListBuffer(pants) 
scala> val everything: ListBuffer[Any] = mxs 
<console>:6: error: type mismatch; 
found : scala.collection.mutable.ListBuffer[String] 
required: scala.collection.mutable.ListBuffer[Any] 
    val everything: ListBuffer[Any] = mxs 

Nawet String jest podtypem scala.Any, Scala nadal nie pozwalają przypisać MXS do wszystkiego. Aby zrozumieć dlaczego, załóżmy ListBuffer jest kowariantna i następujący fragment kodu działa bez problemu kompilacji:

scala> val mxs: ListBuffer[String] = ListBuffer("pants") 
    mxs: scala.collection.mutable.ListBuffer[String] = 
     ListBuffer(pants) 
    scala> val everything: ListBuffer[Any] = mxs 
    scala> everything += 1 
    res4: everything.type = ListBuffer(1, pants) 

można dostrzec ten problem? Ponieważ wszystko jest typu Any, możesz przechowywać wartość całkowitą w zbiorze łańcuchów. To jest katastrofa, która czeka, aby się wydarzyć. Dokładnie to samo dzieje się z tablicami Java. Aby uniknąć tego rodzaju problemów, zawsze dobrze jest, aby zmienne obiekty były niezmienne. Następne pytanie dotyczy tego, co dzieje się w przypadku niezmiennego obiektu do kolekcji. Okazuje się, że dla niezmiennych obiektów kowariancja wcale nie jest problemem. Jeśli zastąpisz ListBuffer niezmienną Listą, możesz wziąć instancję List [String] i przypisać ją do List [Any] bez problemu.

scala> val xs: List[String] = List("pants") 
xs: List[String] = List(pants) 
scala> val everything: List[Any] = xs 
everything: List[Any] = List(pants) 

Jedynym powodem, dla którego to zadanie jest bezpieczne, jest fakt, że lista jest niezmienna. Możesz dodać 1 do Xs List, a ona zwróci nową Listę typu Dowolny.

scala> 1 :: xs 
res5: List[Any] = List(1, pants) 

Ponownie, ten dodatek jest bezpieczny, ponieważ minusy (: :) metoda zawsze zwraca nową listę, a jego rodzaj zależy od rodzaju elementów na liście. Jedynym typem, który może przechowywać wartość całkowitą i wartość odniesienia, jest scala.Any. Jest to ważna właściwość do zapamiętania na temat wariancji typu, gdy mamy do czynienia z obiektami zmiennymi/niezmiennymi.

Najlepszym sposobem zrozumienia kontrawariancji jest dostrzeżenie problemu, który pojawia się, gdy jest nieobecny. Spróbuj zlokalizować problem w następującym przykładzie kodu Java:

Object[] arr = new int[1]; 
arr[0] = "Hello, there!"; 

Kończymy przypisywanie łańcucha do tablicy liczb całkowitych. Java przechwytuje ten błąd w czasie wykonywania, rzucając wyjątek ArrayStoreException.Scala zatrzymuje tego rodzaju błędy podczas kompilacji, wymuszając, że typy parametrów są albo sprzeczne, albo niezmienne.

Mam nadzieję, że to pomoże.

+0

To jest bardzo dobra odpowiedź. –

Powiązane problemy