2011-01-07 17 views
15

que tienen una clase abstracta:método genérico Scala anulando

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

múltiples clases se 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, utilizando los genéricos (o algo así) para hacer que los métodos primordiales ¿tiene el tipo de parámetro de la subclase implementándolo? De esta manera:

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

que estaba pensando algo a lo largo de la línea de los siguientes, pero eso no parece 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.

Respuesta

17
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 todos comparten Foo como una superclase, pero para mantener el valor de retorno de bar2 (o los parámetros a bar1 o bar2) en un entorno en el que todo lo que sabe acerca de su objeto (digamos que se llama obj) es que es Foo, necesita usar el tipo obj.T como el tipo de la variable.

+0

Esto hizo posible lo que pensaba que hacer. Entonces, al usar "tipo T <: Foo", creo un tipo T que es Foo o cualquiera de sus subclases, y luego puedo usar ese tipo dentro de la clase como cualquier otro tipo, en cualquier lugar que desee. Ya sea como un parámetro, tipo de var, valor de retorno, ... ¿Correcto? Muchas gracias. En otros comentarios; Estoy gratamente sorprendido por la rapidez de las respuestas a esta publicación. Sombreros fuera :-) –

+0

Estás en lo correcto. –

2

T debe ser un parámetro de tipo en la clase Foo de la que hereda, no en los métodos mismos.

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 
} 

diferentes subclases de Foo en realidad no tienen un supertipo común en esta versión del código, porque se extienden desde diferentes parametrizaciones de Foo. Puede usar métodos parametrizados que se refieran al Foo[T] cuando necesite trabajar con el supertipo común, pero tiendo a preferir la solución de tipo abstracto que publiqué en mi otra respuesta, porque no filtra los detalles de los genéricos a todos los otras funciones que tienen que ver con Foos.

0

Puede parametrizar Foo para llevar a cabo algunos de los efectos 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 forma sencilla de hacerlo con las características actuales de la Scala.

Además, parte de lo que parece querer no tiene sentido si proporciona una implementación real en Foo. Si Foo se compromete a tomar cualquier Foo pero le otorga un método que insiste solamente en Food, se romperá si le pasa una subclase diferente de Foo (por ejemplo, Fool). Entonces 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! 
+0

Creo que podemos confiar en que el programador no defina 'Fool' como un' Foo [Food] 'a menos que eso sea lo que realmente quiere. –

+1

Creo que podemos confiar en que el programador no tiene la intención de definir 'Fool' como' Foo [Food] ', pero ocurren errores. Si los errores no ocurrieron, probablemente no necesitaríamos ningún tipo de verificación :) –

12

Haciendo segunda versión de Ken Blum un poco más agradable que puede utilizar los tipos de asistencia:

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

Lo ideal es combinar cosas mencionadas anteriormente, es decir,

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

"[T <: Foo [T]]" significa T es subclase de Foo [T], Y "auto: T =>" significa que Foo [T] es subclase de T, y juntos que es un poco extraña forma de decir que Foo [T] es exactamente igual a T.

Sólo con que podía hacer siguiente código de compilación y el trabajo 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 
    }   
    } 

} 
Cuestiones relacionadas