具有泛型和协方差的Scala类型推断,scalac中可能存在的问题

Art*_*sov 2 generics inheritance scala

我有一个我今天发现的问题清单.我是scala新手,因此我的问题可能微不足道.

我们假设我们有一个像这样的类:

abstract class A[+T] { def foo[S >: T](x: S): String }
Run Code Online (Sandbox Code Playgroud)

我们应该提供有用的子类和一些功能.

1)我的第一次尝试看起来像这样:

class B extends A[String] { def foo(x: String) = x }
Run Code Online (Sandbox Code Playgroud)

但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 }
      ^
Run Code Online (Sandbox Code Playgroud)

首先,为什么scalac没有推断foo的泛型类型参数,它真的是一个复杂的任务吗?

2)接下来的尝试看起来更好,应该被接受,我认为:

class B extends A[String] { def foo[String](x: String) = x }
Run Code Online (Sandbox Code Playgroud)

但现在编译器让我大开眼界:

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 }
                                                         ^
Run Code Online (Sandbox Code Playgroud)

看起来像是String和java.lang.String不匹配的问题.这是第二个问题:它真的是一个错误吗?

3)最后我决定将String类型切换为Date作为参数:

import java.util.Date
class B extends A[Date] { def foo[Date](x: Date) = x.toString }
Run Code Online (Sandbox Code Playgroud)

它编译时没有任何警告.最后一个问题是:为什么?我的第二个和第三个片段有什么区别?

顺便说一句,scalac版本是 2.9.1.final

Mal*_*lio 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 }
Run Code Online (Sandbox Code Playgroud)

是的,S >: String因为String是最终的,所以有点无意义,但规范无法划分出每一个可能的边缘情况.

注意 我也是一个tyro,所以如果我的回答错误/误导,不要感到震惊.请记住:这个建议保证价值是您支付的两倍,或者您的钱被高兴地退还.

TOLD YA大错误:我颠倒了超类和子类.与三振出局一致的更正.

示例 OP不清楚为什么所有这些都是必要的.考虑以下功能

def foo3(a : A[String]) = a.foo(3)
Run Code Online (Sandbox Code Playgroud)

这是合法的,因为3(作为java.lang.Integer)是一个Object的例子,它是A的祖先.如果B.foo被定义为接受一个字符串而没有别的,则B的一个实例无法传递给foo3 .

  • 相当不错,除了`S>:T`表示`S`必须是`T`的_super_类型,而不是后代.因此,对于isntance,这个调用是有效的:`new B.foo(new java.util.Date)`,因为`S`被推断为`AnyRef`,`String`的超类型(和`java.util. Date`). (2认同)