为 Scala 特征中的方法指定具体类型

Specify concrete type for methods in Scala trait

我想在 Scala 特征中定义一个方法,其中方法的参数和 return 类型对应于扩展特征的相同具体 class。我试过类似下面的东西:

trait A {
  def foo(obj: this.type): this.type
}

final case class B(val bar: Int) extends A {
  override def foo(obj: B): B = {
    B(obj.bar + this.bar)
  }
}

object Main {
  def main(args: Array[String]) = {
    val b1 = new B(0)
    val b2 = new B(0)
    val b3: B = b1.foo(b2)
  }
}

但是,尝试编译此代码会出现以下错误:

Test.scala:5: error: class B needs to be abstract. Missing implementation for:
  def foo(obj: B.this.type): B.this.type // inherited from trait A
case class B(val bar: Int) extends A {
           ^
Test.scala:6: error: method foo overrides nothing.
Note: the super classes of class B contain the following, non final members named foo:
def foo: ((obj: _1.type): _1.type) forSome { val _1: B }
  override def foo(obj: B): B = {
               ^
2 errors

这里显然我对 Scala 类型系统有一些误解。 class Bfoo 的签名是我想要的,但我不知道如何正确定义 A 中的方法(或者如果这是甚至可能)。 this question 似乎在问一些非常相似的问题,但我没有立即看出答案如何适用于我的情况。

类型注解this.type表示你只能returnthis。因此,在那种情况下,您可能不会 return B 的另一个实例,方法参数也是如此。

如果这只是关于 return 类型,一个解决方案是要求 foo 到 return 类型 A 的东西,[= 中的覆盖方法13=] 可以将 return 类型专门化为 return B.

但是,由于您还有一个参数希望成为子类型的类型,因此您可以使用 Self Recursive Type。下面的例子编译并且应该做你想做的事。

  trait A[S <: A[S]] {
    def foo(obj: S): S
  }

  case class B(val bar: Int) extends A[B] {
    override def foo(obj: B): B = {
      B(obj.bar + 1)
    }
  }

考虑类型class解决方案

case class B(bar: Int)

// type class
trait Fooable[A] {
  def foo(x: A, y: A): A
}

// proof that B satisfies Fooable constraints
implicit val fooableB: Fooable[B] = new Fooable[B] {
  override def foo(x: B, y: B): B = B(x.bar + y.bar)
}

// a bit of syntax sugar to enable x foo y
implicit class FooableOps[A](x: A) {
  def foo(y: A)(implicit ev: Fooable[A]) = ev.foo(x,y)
}

val b1 = B(1)
val b2 = B(41)
b1.foo(b2)
// B(42)

哪个 Scala 3 simplifies

case class B(bar: Int)

// type class
trait Fooable[A] {
  extension (x: A) def foo (y: A): A
}

// proof that B satisfies Fooable constraints + syntactic sugar
given Fooable[B] with
   extension (x: B) def foo (y: B): B = B(x.bar + y.bar) 


val b1 = B(1)
val b2 = B(41)
b1.foo(b2)
// B(42)

查看 Scala 常见问题解答:How can a method in a superclass return a value of the “current” type?