Scala通用子类型参数

时间:2016-08-19 23:02:05

标签: scala generics subtyping

为什么编译器将t1 ++ t2结果视为List [Any]?连接两个S类型的列表应该只返回一个S类型的列表。

// 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 

2 个答案:

答案 0 :(得分:3)

以下是我认为正在发生的事情。首先,S 在任何地方都是相同的类型,这里没有魔力。让我们看一下第一个例子:

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)

正如您所看到的,Scala正确地找到了IntDouble最接近的共同祖先,以及它AnyVal。因此,在这种情况下,SAnyVal

现在让我们试试这个:

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
                                                      ^

出了什么问题?此错误消息表示++的结果以某种方式List[Any]而不是预期的S。这是为什么?让我们看一下++签名(简化,真实签名更长):

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

因此Scala需要找到A的最近祖先和other的实际类型参数。唯一的问题是:它需要在您定义B的位置找到cons,而不是在以后应用它的位置(B不是cons的免费参数) 。唯一的信息是SList[Any]的上限,因此B定义cons的唯一安全解决方案是最通用的,即Any。这意味着++的结果为List[Any],并且它不适合S。因此错误。

第三个例子:

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)

为什么这样做?这里t1t2具有完全相同的类型,无论S是什么(以后可以推断出S)。所以B == S,结果是List[S]。同样在这种特殊情况下,SIntDouble最接近的共同祖先。

答案 1 :(得分:1)

List[Any] ++ List[Any]List[Any]并不保证SList[Any]的子类型也具有S ++ S的属性S,所以编译器会回到List[Any]