有关 Scala 类型系统的一些背景知识。
语法B1 >: String
意思是B1
is a 超型 of String
. So B1
不太具体,并且不能转换为String
。反过来,B1 <: String
将是一个subtype关系。
的定义Map
特质是Map [A, +B]
, where A
代表密钥的类型和B
值的类型。这+B
符号表示Map
is 协变在密钥类型中,这意味着T <: S
暗示Map[A, T] <: Map[A, S]
.
完整类型Map.+
方法是+ [B1 >: B] (kv: (A, B1)): Map[A, B1]
。协方差为B
使用某种力量B1 >: B
。这是它如何工作的示例:给定地图m: Map[String, String]
添加类型不太具体的键值对kv : (String, Any)
将导致地图不太具体,(m + kv): Map[String, Any]
.
最后一点说明了您的问题ParamMap
定义。根据Map
接口,应该能够添加类型的键Any
到类型的地图ParamMap <: Map[String, String]
并得到一个Map[String, Any]
。但你试图定义ParamMap.+
总是回来ParamMap[String, String]
,这与Map.+
.
解决问题的一种方法是给出ParamMap
显式类型参数,类似于(警告未经测试),
class ParamMap[B](val pairs:List[(String,String)] = Nil) extends Map[String, B] {
...
override def + [B1 >: B](kv: (String, B1)) = new ParamMap[B1](kv :: pairs)
}
但这可能不是您想要的。我认为没有办法将值类型修复为String
并实施Map[String, String]
界面。
鉴于上述所有内容,为什么您答案中的代码可以编译?您实际上已经发现了 Scala 模式匹配的局限性(不健全),它可能导致运行时崩溃。这是一个简化的示例:
def foo [B1 >: String](x: B1): Int = {
val (s1: Int, s2: Int) = (x, x)
s1
}
虽然可以编译,但它没有做任何有用的事情。事实上,它总是会崩溃MatchError
:
scala> foo("hello")
scala.MatchError: (hello,hello) (of class scala.Tuple2)
at .foo(<console>:9)
at .<init>(<console>:10)
at .<clinit>(<console>)
...
在你的回答中,你基本上告诉编译器将B1
实例到一个String
,如果转换不起作用,您将遇到运行时崩溃。相当于不安全的强制转换,
(value: B1).asInstanceOf[String]