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 {
        ...
    }
} 

Возможно ли, используя generics (или что-то), чтобы заставить переопределяющие методы иметь тип параметра подкласса, реализующего его? Вот так:

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{
       ...
    }
}

Любая помощь очень ценится!

Спасибо.

Ответы

Ответ 1

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 как тип переменной.

Ответ 2

Чтобы сделать вторую версию Ken Blum немного лучше, вы можете использовать типы self:

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
}

Ответ 3

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.

Ответ 4

В идеале вы комбинируете вещи, упомянутые выше, т.е.

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
    }        
  }

}

Ответ 5

Вы можете параметризовать 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...

Если вы хотите исключить второй случай, нет простого способа сделать это с текущими функциями в Scala.

Кроме того, некоторые из того, что вам кажется, не имеет смысла, если вы даете фактическую реализацию в Foo. Если Foo promises взять любой Foo, но вы даете ему метод, который настаивает только на Food, он сломается, если вы передадите ему другой подкласс Foo (например, 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!