método genérico de Scala anulando
override generic-method (5)
Tengo una clase abstracta:
abstract class Foo(...){
def bar1(f : Foo) : Boolean
def bar2(f : Foo) : Foo
}
las clases múltiples extienden Foo y anulan los métodos
class FooImpl(...) extends Foo{
override def bar1(f : Foo) : Boolean {
...
}
override def bar2(f : Foo) : Foo {
...
}
}
¿Es posible, usar genéricos (o algo) para hacer que los métodos de reemplazo tengan el tipo de parámetro de la subclase que lo implementa? Me gusta esto :
class FooImpl(...) extends Foo{
override def bar1(f : FooImpl) : Boolean {
...
}
override def bar2(f : FooImpl) : FooImpl {
...
}
}
Estaba pensando algo en la línea de lo siguiente, pero eso no pareció funcionar ...
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{
...
}
}
¡Cualquier ayuda es muy apreciada!
Gracias.
Lo ideal es combinar las cosas mencionadas anteriormente, es decir,
trait Foo[T <: Foo[T]] { self:T =>
"[T <: Foo [T]]" significa que T es la subclase de Foo [T], y "self: T =>" significa que Foo [T] es una subclase de T, y juntos es una forma un poco rara de decir que Foo [T] es exactamente igual a T.
Solo con eso pude hacer el siguiente código compilar y trabajar como se esperaba:
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
}
}
}
Para hacer que la segunda versión de Ken Blum sea un poco más agradable, puedes usar los tipos propios:
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
}
Puedes parametrizar Foo
para lograr algo del efecto fácilmente:
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...
Si desea descartar el segundo caso, no hay una forma sencilla de hacerlo con las características actuales en Scala.
Además, algo de lo que parece querer no tiene sentido si le das una implementación real en Foo
. Si Foo
promete tomar cualquier Foo
pero le das un método que insiste solo en un Food
, se romperá si pasas una subclase diferente de Foo
(por ejemplo, Fool
). Así que el compilador no te dejará hacer eso.
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!
T
debe ser un parámetro de tipo en la clase Foo
la que hereda, no en los métodos en sí.
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
}
Las diferentes subclases de Foo
realidad no tienen un supertipo común en esta versión del código, ya que se extienden desde diferentes parametrizaciones de Foo
. Puede usar métodos parametrizados que se refieren a Foo[T]
cuando necesita trabajar con el supertipo común, pero tiendo a preferir la solución de tipo abstracto que publiqué en mi otra respuesta, porque no se filtran los detalles de los genéricos. Todas las demás funciones que tienen que ver con Foos.
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
}
En esta versión, las diferentes subclases de Foo
comparten Foo
como una superclase, pero para mantener el valor de retorno de bar2
(o los parámetros de bar1
o bar2
) en una configuración donde todo lo que sabe sobre su objeto (digamos que se llama obj
) es que es un Foo
, necesitas usar el tipo obj.T
como el tipo de la variable.