2016-08-19 4 views
3

Warum behandelt der Compiler t1 ++ t2 Ergebnis als Liste [Any]? Die Verkettung zweier Listen vom Typ S sollte nur eine Liste vom Typ S zurückliefern.Scala generische Subtyp Parameter

// compiles 
def cons[S <: List[Any]](t1: S, t2: S): S = t1 

// type mismatch; found List[Any] required S 
def cons[S <: List[Any]](t1: S, t2: S): S = t1 ++ t2 

Antwort

3

Hier ist, was ich glaube passiert. Zunächst einmal Sist den gleichen Typ überall gibt es keine Magie hier. Schauen wir uns das erste Beispiel aussehen:

scala> def cons[S <: List[Any]](t1: S, t2: S): S = if(t1.isEmpty) t1 else t2 
cons: [S <: List[Any]](t1: S, t2: S)S 

scala> cons(List(1), List(2.0)) 
res21: List[AnyVal] = List(2.0) 

Wie Sie Scala richtig den nächsten gemeinsamen Vorfahren für Int gefunden sehen und Double, und es ist AnyVal. In diesem Fall ist also SAnyVal.

lassen Sie uns dies nun versuchen:

scala> def cons[S <: List[Any]](t1: S, t2: S): S = t1 ++ t2 
<console>:11: error: type mismatch; 
found : List[Any] 
required: S 
     def cons[S <: List[Any]](t1: S, t2: S): S = t1 ++ t2 
                ^

Was ist los? Diese Fehlermeldung bedeutet, dass das Ergebnis von ++ ist irgendwie List[Any] statt der erwarteten S. Warum das? Lassen Sie sich bei ++ Unterschrift aussieht (vereinfachte, ist echte Unterschrift länger):

def ++[B >: A](other: List[B]): List[B] = ??? 

So braucht Scala die nächsten Vorfahren der A und die tatsächlichen Typen Parameter von other zu finden. Das einzige Problem ist: Es muss B an dem Punkt finden, wo Sie cons definieren, nicht wo Sie es später anwenden (B ist kein freier Parameter für cons). Die einzige Information ist die obere Grenze von S, und es ist List[Any], so dass die einzige sichere Lösung für B am Punkt der Definition von cons die allgemeinste ist, d.h. Any. Das bedeutet, dass das Ergebnis ++ ist List[Any], und es passt nicht S. Daher der Fehler.

Drittes Beispiel:

scala> def cons[S <: Any](t1: List[S], t2: List[S]): List[S] = t1 ++ t2 
cons: [S](t1: List[S], t2: List[S])List[S] 

scala> cons(List(1), List(1.0)) 
res0: List[AnyVal] = List(1, 1.0) 

Warum funktioniert das? Hier haben sowohl t1 als auch t2 genau den gleichen Typ, egal was S ist (und S kann später abgeleitet werden). So B == S und das Ergebnis ist List[S]. Auch in diesem speziellen Fall ist S der nächste gemeinsame Vorfahre von Int und Double.

+0

In Ihrem dritten Beispiel sagen Sie "Hier haben beide t1 und t2 genau den gleichen Typ, egal was S ist". Wie kann das sein? List [Int] List [Double] sind verschiedene Typen, nicht wahr? – Samar

+0

Ich denke, die Verwirrung entsteht, weil verschiedene Typen anfangs als Argumente übergeben werden können, aber der Typ für S wird der nächste gemeinsame Vorfahre der verschiedenen Typen sein. – Samar

+0

Ich zweite @ Samars Fragen. Ich sehe keinen Unterschied zwischen '(t1: S, t2: S)' und '(t1: Liste [S], t2: Liste [S])'. In beiden Fällen haben t1 und t2 genau den gleichen Typ. Natürlich können Sie 'List [Int]' und 'List [String]' übergeben, aber in diesem Fall ist der nächstliegende Vorfahre 'List [Any]', also ist das, was 'S' ist. Können Sie ein Beispiel für 'def cons [S <: Liste [Beliebige]] (t1: S, t2: S): S 'geben, wo wir einen Fehler hätten (daher lässt der Compiler das nicht zu)? Was auch immer Sie übergeben, Compiler wird schließen, dass "S" der nächste Vorfahre ist und ** das ist, was zurückgegeben wird **. Abgeleiteter Typ "S". Es sollte mit 'S' funktionieren, oder? – slouc

1

List[Any] ++ List[Any] ein List[Any] ist nicht S garantieren, ein Subtyp von List[Any] haben auch die Eigenschaft von S ++ S ist ein S, so dass die complier fällt auf List[Any] zurück.

+0

ich denke, Victors Antwort erklärt wirklich was los ist. Ich bin mir noch nicht 100% sicher, aber ich denke, wenn die zwei tatsächlichen Argumente t1/t2 an die cons-Methode übergeben werden, werden die Typen der übergebenen Argumente auf ihren nächsten gemeinsamen Vorfahren zurückverfolgt, und das ist der Typ von S. Not der übergebene Typ des Arguments. – Samar

Verwandte Themen