为什么scalac不相信某个方法不符合所需的类型签名?

时间:2014-08-28 14:19:52

标签: scala types

为什么不编译?
给出的错误是class SomeElement needs to be abstract, since method eval in trait Element of type [T <: Typed]=> scala.util.Try[T] is not defined

我无法理解为什么SomeElement上定义的方法eval不满足类型约束。

据我了解,eval应该返回包含在Try子类Typed中的内容。 Try在其类型参数中是协变的。 evalSomeElement的实施将返回NumberLikeNumberLike子类Typed。出了什么问题?

import scala.util.Try

trait Element {
   def eval[T <: Typed]: Try[T]
}

trait Typed

case class NumberLike(n: Long) extends Typed

case class SomeElement(n: Long) extends Element {
  def eval = Try {
    NumberLike(n)
  }
}

object app extends Application {
  println (SomeElement(5).eval)
}

尝试在SomeElement中向eval添加显式类型参数也无济于事:

case class SomeElement(n: Long) extends Element {
  def eval[NumberLike] = Try {
    NumberLike(n)
  }
}

将SomeElement的定义更改为上面给出:

  found   : <empty>.NumberLike
  required: NumberLike(in method eval)
    NumberLike(n)

编辑我真的想知道为什么这不能编译。问题的解决方法很有帮助,但我真的想知道这里发生了什么。

2 个答案:

答案 0 :(得分:2)

类型参数T是在函数上定义的,而不是在封闭类型Element上定义的,它不能通过继承来“擦除”,并且必须保留在重写函数上({{3 }})。

将type参数移动到Element定义使其工作如下。

trait Element[T <: Typed] {
   def eval: Try[T]
}

trait Typed

case class NumberLike(n: Long) extends Typed

case class SomeElement(n: Long) extends Element[NumberLike] {
  def eval = Try {
    NumberLike(n)
  }
}

或使用类型成员:

trait Element {
   type T <: Typed
   def eval: Try[T]
}

trait Typed

case class SomeElement(n: Long) extends Element {
  type T = NumberLike
  def eval = Try {
    NumberLike(n)
  }
}

答案 1 :(得分:0)

既然有人给出了解决方案,我就试图解释原因。

在java中,这样的东西是非法的:

class A {
  <T> T f() {return null;}
  Object f() {return null;}
}

但是在scala中,这是合法的:

class A {
  def f[T](): T = ???
  def f(): Any = ???
}

主要区别在于:scala处理2个具有相同名称和参数的方法列表不同,如果其中一个具有类型参数而另一个没有。(我认为scala方法的签名包括类型参数,如果我是正确的我错:))

因此,在您的情况下,您不能通过普通方法覆盖带有类型参数的方法。(它们具有不同的签名)

我不确定哪一个更好,我个人更喜欢scala规则:)