Scala 泛型子类型参数

标签 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 

最佳答案

这就是我认为正在发生的事情。首先,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的实际类型参数。唯一的问题是:它需要在你定义 cons 的地方找到 B,而不是你稍后应用它的地方(B 不是一个cons 的免费参数)。唯一的信息是 S 的上界,它是 List[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 最近的共同祖先。

关于Scala 泛型子类型参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39048763/

相关文章:

typescript - TypeScript 中的递归泛型

types - 通配符模式覆盖多态变体的子类型约束

scala - 迭代器上的Scala映射不会产生副作用

scala - Spark 数据框修剪列并转换

java - 查找 ArrayList<Object> 中的特定类型(即 Object = String 等)

scala - 类型类接口(interface)的DataSet/DataStream

c++ - 实现细节继承?

scala - Scala 的哪些特性允许使用 Props[SomeActor] 语法

scala - Akka 路由 : Reply's send to router ends up as dead letters

java - 在 Java 中创建类型的通用实例