переопределение универсального метода scala - PullRequest
18 голосов
/ 07 января 2011

У меня есть абстрактный класс:

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

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

Спасибо.

Ответы [ 5 ]

20 голосов
/ 07 января 2011
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 в качестве типа переменной.

12 голосов
/ 07 января 2011

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

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
}
2 голосов
/ 07 января 2011

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.

1 голос
/ 11 августа 2011

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

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

}
0 голосов
/ 07 января 2011

Вы можете параметризовать 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 обещает взять любой 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!
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...