2010-11-29 15 views
8

Me gustaría tener un rasgo sellado que tiene un método declarado que devuelve la clase real que extiende el rasgo. ¿Debo usar un tipo abstracto, un tipo de parámetro o ? ¿Hay alguna otra manera agradable de resolver esto?Extendiendo un rasgo y tipos

sealed trait Foo { 
    type T 
    def doit(other: T): T 
} 

o

sealed trait Foo[T] { 
    def doit(other: T): T 
} 

Tenga en cuenta que T debe haber un subtipo de Foo en este ejemplo. Si lo hago así la información de tipo siente demasiado repitió:

case class Bar(name: String) extends Foo[Bar] { 
    def doit(other: Bar): Bar = ... 
} 

Respuesta

2

Puede reducir la repetición de un tanto por tener su método doit devolver una función de fábrica:

trait Foo[T] { 
    self: T => 
    def doit: T => T 
} 

case class Bar(name: String) extends Foo[Bar] { 
    // note: types omitted 
    def doit = { other => Bar(name + other.name) } 
} 

No es posible hacer lo mismo con un tipo abstracto:

trait Foo { 
    self: T => // won't compile because T isn't defined yet 
    type T 
    def doit: T => T 
} 
1

EDITAR - A continuación es mi respuesta original. Su comentario indica que desea devolver una instancia arbitraria de un tipo coincidente, pero realmente no creo que esto sea de ninguna manera razonable. Supongamos que fuera, a través de la sintaxisT.type:

trait T { def foo : T.type } 

trait U extends T { def foo = new U } //must be a U 

class W extends U 

val w : W = (new W).foo //oh dear. 

Esto es realizable a través de this.type:

scala> trait T { 
| def foo : this.type 
| } 
defined trait T 

scala> class W extends T { 
| def foo = this 
| } 
defined class W 

scala> (new W).foo 
res0: W = [email protected] 

scala> res0.foo 
res1: res0.type = [email protected] 

Y luego también:

scala> ((new W) : T) 
res4: T = [email protected] 

scala> res4.foo.foo.foo 
res5: res4.type = [email protected] 
+0

suena familiar. ¿Podrías dar un ejemplo de esto? – chrsan

+0

¿Qué parece no funcionar si quiero devolver una nueva instancia del mismo tipo? – chrsan

+0

Sí - 'this.type' depende de la ruta: solo es válido para devolver la instancia actual –

4

En su mayoría son intercambiables. De acuerdo con Odersky, la razón fue principalmente para completar: que de manera similar al hecho de que los métodos y campos (valores) pueden ser abstractos o pasar como parámetros, también lo pueden hacer los tipos.

Es mejor usar un tipo abstracto cuando intenta mezclar varios rasgos que usan el mismo nombre. Con los parámetros de tipo que necesita para aprobar explícitamente el tipo de cada

Aquí está un artículo que explica todo esto: http://www.artima.com/weblogs/viewpost.jsp?thread=270195

1
trait Foo[A <: Foo[A]] 

Esta característica sólo puede ser mezclado en si A es un subtipo de Foo [A] y el único tipo satisfactorio que es la clase en la que Foo se está mezclando. Vi esta solución en los rasgos de Mapper en Lift.

+0

No del todo.Pruebe 'class Bar extends Foo [Bar]' y 'class Baz extiende Foo [Bar]'. –

2

puede escribir:

trait Foo[T] { 
    self:T => 
    def doit(other: T): T 
} 

case class Bar(name: String) extends Foo[Bar] { 
    def doit(other: Bar): Bar = ... 
} 

La diferencia en su ejemplo es el bar no se pueden crear instancias de cualquier otro modo (por ejemplo case class Bar(name: String) extends Foo[String]).