我有一个我今天发现的问题的简短列表。 我是scala新手,因此我的问题可能很简单。
我们假设我们有一个像这样的类:
abstract class A[+T] { def foo[S >: T](x: S): String }
我们应该为有用的子类提供一些功能。
1)我的第一次尝试看起来像这样:
class B extends A[String] { def foo(x: String) = x }
但是scalac编译器不同意消息:
xxx@yyy:~$ scalac covariant.scala
covariant.scala:3: error: class B needs to be abstract, since method foo in class A of type [S >: String](x: S)String is not defined
class B extends A[String] { def foo(x: String) = x }
^
首先,为什么scalac没有推断foo的泛型类型参数,这真的是一项复杂的任务吗?
2)接下来尝试看起来更好,应该被接受,我认为:
class B extends A[String] { def foo[String](x: String) = x }
但是现在编译器让我大开眼界:
covariant.scala:3: error: type mismatch;
found : String(in method foo)
required: java.lang.String
class B extends A[String] { def foo[String](x: String) = x }
^
看起来像String和java.lang.String不匹配的问题。这是第二个问题:这真的是一个错误吗?
3)最后我决定将String类型切换为Date作为参数:
import java.util.Date
class B extends A[Date] { def foo[Date](x: Date) = x.toString }
它编译时没有任何警告。最后一个问题是:为什么?我的第二个和第三个片段之间有什么区别?
顺便说一下,scalac版本是2.9.1.final
答案 0 :(得分:7)
真的,你所有的问题都源于同样的误解。
你定义为“一个带有类型参数的类和一个方法foo,它可以作用于T 的任何类型 下降,T从中下载。
你提供了一个带有foo的类,它可以作用于T而不是必然 后代(实际上,因为参数是协变的,foo实际上可以用于后代但是规范不是足以让人抓住那个超级班的T.
在其他两个问题中,您不小心重用了现有类型的名称。当你编写foo [Date]或foo [String]时,你并没有像你显然那样提到java.lang.String或java.lang.Date,而是指同名的新类型!这就是为什么你看到String和java.lang.String不匹配的原因 - 它们是两种不同的类型。
请改为尝试:
abstract class A[+T] { def foo[S >: T](x: S): String }
class B extends A[String] { def foo[S >: String](x: S) = x.toString }
是的, S >: String
是没有意义的,因为String是最终的,但规范无法划分出每一个可能的边缘情况。
注意我也是一个tyro,所以如果我的回答错误/误导,请不要感到震惊。请记住:这个建议保证价值是您支付的两倍,或者您的钱被高兴地退还。
TOLD YA 大错误:我推翻了超类和子类。内联修正与三振出。
示例 OP不清楚为什么所有这些都是必要的。考虑以下功能
def foo3(a : A[String]) = a.foo(3)
这是合法的,因为3(作为java.lang.Integer)是一个Object的例子,它是A的祖先。如果B.foo被定义为接受一个字符串而没有别的,B的实例不能是传入foo3。