web-dev-qa-db-ja.com

scalaジェネリックメソッドのオーバーライド

私は抽象クラスを持っています:

abstract class Foo(...){
   def bar1(f : Foo) : Boolean
   def bar2(f : Foo) : Foo
}

複数のクラスがFooを拡張し、メソッドをオーバーライドします

class FooImpl(...) extends Foo{
    override def bar1(f : Foo) : Boolean {
        ...
    }
    override def bar2(f : Foo) : Foo {
        ...
    }
} 

ジェネリックス(または何か)を使用して、オーバーライドするメソッドにそれを実装するサブクラスのパラメータータイプを持たせることは可能ですか?このような :

class FooImpl(...) extends Foo{
    override def bar1(f : FooImpl) : Boolean {
        ...
    }
    override def bar2(f : FooImpl) : FooImpl {
        ...
    }
}

私は次のようなことを考えていましたが、うまくいかなかったようです...

abstract class Foo(...){
    def bar1[T <: Foo](f : T) : Boolean
    def bar2[T <: Foo](f : T) : T
}

class FooImpl(...) extends Foo{
    override def bar1[FooImpl](f : FooImpl) : Boolean {
       ...
    }
    override def bar2[FooImpl](f : FooImpl) : FooImpl{
       ...
    }
}

どんな助けでも大歓迎です!

ありがとうございました。

19
Jannik Luyten
abstract class Foo{
   type T <: Foo
   def bar1(f:T):Boolean
   def bar2(f:T):T
}

class FooImpl extends Foo{
   type T = FooImpl
   override def bar1(f:FooImpl) = true
   override def bar2(f:FooImpl) = f
}

このバージョンでは、Fooの異なるサブクラスはすべてスーパークラスとしてFooを共有しますが、bar2の戻り値(またはbar1またはbar2へのパラメーター)をすべての知識がある設定で保持しますオブジェクト(たとえば、objという名前)はFooであるため、変数の型としてobj.T型を使用する必要があります。

20
Ken Bloom

Ken Blumの2番目のバージョンを少し良くするために、セルフタイプを使用できます。

abstract class Foo[T] { self:T =>
   def bar1(f:T):Boolean
   def bar2(f:T):T
}

class FooImpl extends Foo[FooImpl]{
   override def bar1(f:FooImpl) = true
   override def bar2(f:FooImpl) = f
}
12
Landei

Tは、メソッド自体ではなく、継承元のFooクラスの型パラメーターである必要があります。

abstract class Foo[T <: Foo[T]]{
   def bar1(f:T):Boolean
   def bar2(f:T):T
}

class FooImpl extends Foo[FooImpl]{
   override def bar1(f:FooImpl) = true
   override def bar2(f:FooImpl) = f
}

Fooの異なるサブクラスは、Fooの異なるパラメーター化から拡張されるため、このバージョンのコードでは実際には共通のスーパータイプを持っていません。一般的なスーパータイプを操作する必要がある場合は、Foo[T]を参照するパラメーター化されたメソッドを使用できますが、ジェネリックスの詳細がリークされないため、他の回答で投稿した抽象型ソリューションを好む傾向がありますFoosを処理する必要がある他のすべての関数に。

2
Ken Bloom

理想的には、上記のものを組み合わせます。

trait Foo[T <: Foo[T]] { self:T =>

「[T <:Foo [T]]」は、TがFoo [T]のサブクラスであることを意味し、「self:T =>」は、Foo [T]がTのサブクラスであることを意味します。これを合わせると、少し奇妙な言い方になります。そのFoo [T]はTとまったく同じです。

それだけで、次のコードをコンパイルして意図したとおりに動作させることができました。

trait Field[T <: Field[T]] { self:T =>

  def x2:T

  def +(that:T):T

  def *(n:BigInt) : T = {
    if(n == 1)
      this
    else if(n == 2)
      this.x2
    else if(n == 3)
      this + this.x2
    else {
      val p = (this * (n/2)).x2
      if (n%2==0)
        p
      else
        p + this
    }        
  }

}
1
Baturinsky

Fooをパラメーター化して、いくつかの効果を簡単に実現できます。

abstract class Foo[F <: Foo[F]] { def f: F }
class Food extends Foo[Food] { def f = this }  // Yay!
class Fool extends Foo[Food] { def f = new Food }  // Uh-oh...

2番目のケースを除外したい場合、Scalaの現在の機能でそれを行う簡単な方法はありません。

また、Fooで実際の実装を行うと、必要と思われるものの一部が意味をなさなくなります。 FooFooを取ることを約束しているが、Foodのみを要求するメソッドを指定した場合、Fooの別のサブクラスを渡すと壊れます。 var] _(例:Fool)。したがって、コンパイラはそれを許可しません。

abstract class Foo { def bar(f: Foo) : Foo }
class Foot extends Foo { def bar(f: Foo) = this }   // Fine!
class Fool extends Foo { def bar(f: Fool) = this }   // No good!
0
Rex Kerr