Scala 泛型子类型参数
Scala generic subtype parameters
为什么编译器将 t1 ++ t2 结果视为 List[Any]?连接两个类型 S 的列表应该 return 仅类型 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
List[Any] ++ List[Any]
是一个List[Any]
不保证S
,List[Any]
的子类型也有属性的S ++ S
是一个S
,所以编译器回落到 List[Any]
。
这是我认为正在发生的事情。首先,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 正确地找到了 Int
和 Double
最近的共同祖先,它是 AnyVal
。所以在这种情况下 S
是 AnyVal
.
现在让我们试试这个:
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]
,因此在 cons
的定义点 B
的唯一安全解决方案是最通用的解决方案,即 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)
为什么这样做?这里 t1
和 t2
具有完全相同的类型,无论 S
是什么(并且 S
可以稍后推断)。所以 B == S
结果是 List[S]
。同样在这个特殊情况下,S
是 Int
和 Double
最近的共同祖先。
为什么编译器将 t1 ++ t2 结果视为 List[Any]?连接两个类型 S 的列表应该 return 仅类型 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
List[Any] ++ List[Any]
是一个List[Any]
不保证S
,List[Any]
的子类型也有属性的S ++ S
是一个S
,所以编译器回落到 List[Any]
。
这是我认为正在发生的事情。首先,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 正确地找到了 Int
和 Double
最近的共同祖先,它是 AnyVal
。所以在这种情况下 S
是 AnyVal
.
现在让我们试试这个:
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]
,因此在 cons
的定义点 B
的唯一安全解决方案是最通用的解决方案,即 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)
为什么这样做?这里 t1
和 t2
具有完全相同的类型,无论 S
是什么(并且 S
可以稍后推断)。所以 B == S
结果是 List[S]
。同样在这个特殊情况下,S
是 Int
和 Double
最近的共同祖先。