如果我要说出关于scala的最令人讨厌的事情,那就是以下代码:
trait G[+T]
class H[+T] extends G[T]
def f[A<:G[X], X<:Int](g :A)
val g :H[Int]
f(g)
编译器推断最后一次调用f [H [Int],Nothing]的类型,并在我面前因为自己的愚蠢而抱怨。
然而,了解scala,它实际上比我更清楚。它背后的原因是什么?由于G和H对于任何类型S都是T,S <: G[X] with H[_] <=> S<: H[X]
是协变的。这个缺点使我设计了一切,避免必须明确指定类型 - 它可能看起来像什么都没有,但当名称成为&时#39;真正的&#39;长度和几乎任何方法都是通用的,并且经常处理两种泛型类型,事实证明大多数代码都是类型声明。
编辑: 上面的案例在下面由Noah解决,但是当派生类与基类不同时,如下所示?
trait G[+X]
class H[+X, Y] extends G[X]
class F extends G[Int]
def f[A<:G[X], X<:Int](g :A) = g
val h: H[Int, String] = ???
val g :F = ???
f(g)
f(h)
答案 0 :(得分:3)
如果你让A
采用类型参数A[_]
我认为你可以让Scala编译器同意你的意见,而不仅仅是制作所有内容Nothing
:
def f[A[_] <: G[_], X <: Int](g: A[X])
作为旁注,我通常会在遇到类型问题时查看scalaz
来源,因为他们经常会遇到类型问题并尽可能地解决它。
<强>更新强>
我上面提供的方法仍然适用于给出的附加约束:
trait G[+X]
class H[+X, Y] extends G[X]
class F extends G[Int]
class I extends G[String]
def f[A[_] <: G[_], X <: Int](g: A[X]) = g
val h: H[Int, String] = new H[Int, String]
val g: F = new F
val i:I = new I
f(g) //works
f(h) //works
f(i) // should fail and does fail