代码之家  ›  专栏  ›  技术社区  ›  Michael Mior

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

  •  0
  • Michael Mior  · 技术社区  · 4 年前

    我想在Scala trait中定义一个方法,其中方法的参数和返回类型都对应于扩展trait的同一个具体类。我尝试了以下方法:

    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类型系统有一些误解。签名 foo 在课堂上 B 这就是我想要的,但我不知道如何在 A (或者如果这是可能的)。看起来像 this question 问的问题非常相似,但我没有立即看到这个答案如何适用于我的情况。

    0 回复  |  直到 4 年前
        1
  •  3
  •   Wellingr    4 年前

    类型注释 this.type 意味着你只能回来 this 。因此,在这种情况下,您可能不会返回另一个实例 B ,方法参数也是如此。

    如果这只是关于返回类型,那么解决方案是要求 foo 返回某种类型的东西 A ,中的覆盖方法 B 可以专门指定要返回的返回类型 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)
        }
      }
    
        2
  •  3
  •   Mario Galic    4 年前

    考虑类型类解决方案

    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?