假设我有一个名为 Marker
的空标记特征和一些由 Marker
绑定的类型参数的函数:
trait Marker
object Marker {
def works[M <: Marker](m:M):M = m
def doesntWork[M <: Marker](f:M => String):String = "doesn't matter"
}
第一个功能正如我所料 . 也就是说,如果传递的参数不是 Marker
,则代码不会编译:
scala> works("a string")
<console>:14: error: inferred type arguments [String] do not conform to method works's type parameter bounds [M <: com.joescii.Marker]
works("a string")
^
<console>:14: error: type mismatch;
found : String("a string")
required: M
works("a string")
^
但是,我能够将参数传递给不符合 Marker
的第二个函数 . 具体来说,我可以传递 String => String
类型的函数,代码很乐意编译并运行:
scala> doesntWork( (str:String) => "a string" )
res1: String = doesn't matter
我希望这个对 doesntWork
的调用无法编译 . 任何人都可以向我解释为什么它编译以及如何更改函数签名以防止类型在这种情况下检查?
完全披露:以上设计的例子是this outstanding issue for lift-ng的简化版本 .
2 回答
M => String
实际上是Function1[M, String]
. 如果你看一下定义:所以
M
变成逆变,这意味着对于M1 >: M2
,Function1[M1, String] <: Function1[M2, String]
,让我们说M1 = Any
然后Function1[Any, String] <: Function1[Marker, String]
.并输入
doesntWork
-f
也是逆变的,这意味着你可以传递小于M => String
的东西,正如我刚才所示,Any => String
小于Marker => String
,所以它完全没问题 .你也可以传递
String => String
,因为你的[M <: Marker]
,最终导致编译器将M
解释为Nothing
,所以即使String => String
也变得大于M => String
.要解决您的问题,只需引入包装器,这将使您的类型不变:
它's usually bad to recommend such implicit conversions, but seems fine here (if you won'吨过度使用
F
):代码由于逆转而编译 . 您可以通过显式提供推断类型参数来查看:
这是一个普遍的问题 . 有各种各样的技术可以解决这个问题,但它们通常归结为将
T
限制为某种类型的实例 .