2016-05-19 12 views
4

Ich studiere gerade die Varianz in der Skala, und ich denke, dass ich die Kontravarianz gut verstehe. Zum Beispiel gibt trait List[-A], ich weiß, dass List[Int] ist ein Supertyp von List[AnyVal]. AberWarum definiert Methode wie "cons [B>: A] (v: B)" akzeptiert Argument des Typs, der nicht Supertyp von A ist?

sagen, dass ich die folgende Eigenschaft haben:

trait List[+A] { 
    def cons(hd: A): List[A] 
} 

Warum ist cons Parametertyp falsch?

Warum ist es notwendig, def cons[B >: A](v: B): List[B] zu haben?

Zum Beispiel:

val animal_list: List[Animal] = List(tiger, dog) 

wenn wir nennen:

animal_list.cons(tiger) 

seit Tiger <: Animal, cons nicht in Problem lief? Seit B ist Tiger und A ist Animal und B >: A ist nicht wahr.

Antwort

5

Warum ist der Parametertyp cons falsch?

trait List[+A] { 
    def cons(hd: A): List[A] 
} 

Compiler geben Sie Fehler:
covariant type A occurs in contravariant position in type A of value hd
weil Methodenparameter als kontra Positionen zählen, aber A covariant ist.

Stellen wir uns vor, dass diese Methodendeklaration kompilieren würde. Dann könnten wir tun:

class ListImpl[A] extends List[A] { 
    override def cons(hd: A): List[A] = ??? 
} 

val strings: List[String] = new ListImpl[String] 
val values: List[Any] = strings // OK, since List[String] <: List[Any] (in List[A], A is covariant) 
values.cons(13) // OK(??), since values's static type is List[Any], so argument of cons should be Any, and 13 conforms to type Any 

Ist die letzte Zeile oben wirklich OK? Wir rufen cons auf values. values ist das gleiche wie strings, und strings ist Objekt des Typs ListImpl[String]. So cons Aufruf in der letzten Zeile erwartet String Argument, aber wir übergeben Int, weil values ist statischer Typ List[Any] und Int entspricht Any. Irgendetwas stimmt hier definitiv nicht - welche Linie ist schuld? Die Antwort lautet: cons Methodendeklaration. Um dieses Problem zu beheben, müssen wir den kovarianten Typ-Parameter A aus der kontravarianten Position entfernen (in cons Deklaration). Alternativ können wir A nicht-kovariant machen.

Siehe auch diese Fragen: #1, #2.

... nicht cons lief in Problem?

trait List[+A] { 
    def cons[B >: A](v: B): List[B] 
} 

val animal_list: List[Animal] = List(tiger, dog) // We are assuming that List.apply and concrete implementation of List is somewhere defined. 

Nein, animal_list.cons(tiger) Aufruf typ korrekt.

Ich gehe davon aus, dass Animal ist üblich, übergeordneter Typ von Dog und Tiger, und dass dog und tiger sind Instanzen von Dog und Tiger sind.

In animal_list.cons(tiger) Aufruf beider A und B Typparameter sind Animal instanziiert, so cons Methode nimmt Form von:

def cons[Animal >: Animal](v: Animal): List[Animal] 

Animal >: Animal Einschränkung erfüllt ist, weil:

Supertype and subtype relationships are reflexive, which means a type is both a supertype and a subtype of itself. [ source ]

Das Argument cons ist Tiger, was dem Typ Animal entspricht, also ist der Methodenaufruf Typ-c orrect.

Beachten Sie, dass, wenn Sie B zwingen Tiger instanziiert werden, wie animal_list.cons[Tiger](tiger), dann wird dieser Aufruf nicht typ richtig, und Sie werden Compiler-Fehler.

Siehe ähnliches Beispiel .

+0

Oh ich sehe, also der Grund, dass "Tiger" trotz "Tiger <: Animal" zu "cons" übergehen konnte, ist, weil 'Tiger' zuerst in' Animal' getypt wird? – testing

+0

@testing - Mit der Deklaration 'def cons [B>: A] (v: B): Liste [B]' können Sie ein beliebiges Objekt an 'cons' übergeben - das Typargument' B' wird immer am häufigsten instanziiert genauer gemeinsamer Typ von "A" und die Art von "cons" -Argument. Das Übergeben des Objekts an die Methode, die das Argument supertype erwartet, ist ein normales OO-Ding - e. G. Sie können Tiger jederzeit passieren, wenn "Animal" erwartet wird. – TeWu

+0

Was ist, wenn ich 'val tiger_list: List [Tiger] = List (tiger)' habe, würde dies bedeuten, dass "tiger_list.con (animal)" gültig ist? sollten wir nicht so etwas haben wie 'def cons [B>: A] [C <: A] (v: B): Liste [C]' – testing