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!