Scala 抽象类中的类型参数`

Scala 抽象类中的类型参数`,scala,Scala,鉴于: ,以及一个实现 scala> abstract class Foo[A] { | def f: A | } defined class Foo 然后是一个实例 scala> class FooImpl extends Foo[Any] { | def f: Any = "foo" | } defined class FooImpl res0具有类型Any,这是我所期望的,因为FooImpl使用Any作为其类型参数来Foo 并

鉴于:

,以及一个实现

scala> abstract class Foo[A] {
     |   def f: A
     | }
defined class Foo
然后是一个实例

scala> class FooImpl extends Foo[Any] {
     |   def f: Any = "foo"
     | }
defined class FooImpl
res0
具有类型
Any
,这是我所期望的,因为
FooImpl
使用
Any
作为其类型参数来
Foo

并且,考虑到第二个实现:

scala> new FooImpl().f
res0: Any = foo
以及实例化和调用
f

scala> class FooImpl2 extends Foo[Any] {
     |   override def f= "foo"
     | }
defined class FooImpl2
为什么
res1
的类型是a
String

鉴于,鉴于:

scala> new FooImpl2().f
res1: String = foo
我可以传递
y
,一个
Int

scala> def g[A](x: A):A = x
g: [A](x: A)A
g

scala> val y: Int = 55
y: Int = 55
并返回一个
AnyVal

最后,

scala> g[AnyVal](y)
res3: AnyVal = 55
按预期返回一个
Int

然而,我本以为
FooImpl2#f
会返回一个
Any
给定
FooImpl2
扩展Foo[Any]


为什么不呢?

当你重写一个方法时,你可以让它返回更具体的类型。在这一行

scala> g(55)
res5: Int = 55

您没有指定返回类型,它被推断为
String

当您重写抽象类或trait的成员时,您可以将其类型缩小为更具体的类型。这里不是这样,因为您依赖类型推断来覆盖def=“foo”,但实际上是
覆盖def:String=“foo”

这是合法的:

override def f= "foo"
String
仍然符合类型参数
A=Any
,但
f
已在
FooImpl2
中细化为
String

下面是一个没有类型参数的示例:

abstract class Foo[A] {
  def f: A
}

class FooImpl2 extends Foo[Any] {
  override def f: String = "foo"
}

g[AnyVal](y)
是一个完全不同的例子。由于您手动将
AnyVal
的类型参数提供给
g
,因此您要求编译器确保
y
AnyVal
,但它是否是某个更具体的类型并不重要(方法返回的更具体的类型将始终向上转换为返回类型)。在
FooImpl2
中,您只需更改
f

的签名,子类就可以缩小它覆盖的方法的返回类型。

Foo[Any]
中的
f
的返回类型为
Any
FooImpl2
Foo[Any]
的子类型,您没有指定
f
s返回类型,因此编译器推断它是
String
,它是
Any
的子类型,因此满足所有约束。

值得注意的是,这在Java中也是可能的(但这只是因为在Java5中引入了协变返回类型),在Scala和Java中,它在JVM级别产生了两种方法:更具体的方法和与实现的签名匹配的合成方法。
abstract class A { def f: Any }
class B extends A { override def f: String = "a" }