2009-04-22 15 views
13

Me gustaría poder usar this.type para definir un método que crea nuevas instancias de una clase de caso inmutable. Algo como esto:¿Por qué no se puede usar this.type para nuevas instancias?

trait Expression 
{ 
    def left : Expression 
    def right : Expression 

    def new_with_changes(l : Expression, r : Expression) : this.type 
} 

case class Derived(left : Expression, right : Expression) 
{ 
    def new_with_changes(l : Expression, r : Expression) : this.type = 
    { 
    new Derived(left, right) 
    } 
} 

Por desgracia, el compilador se queja

test.scala:13: error: type mismatch; 
found : Derived 
required: Derived.this.type 
    new Derived(left, right) 
    ^
one error found 

¿Cómo es que la nueva clase caso no coincide this.type?

Si cambio this.type a Base en Base.new_with_changes y Derived in Derived.new_with_changes que funciona, pero parece que se está perdiendo la elegancia de this.type.

Editar: El verdadero propósito de la pregunta es por qué no tener una manera equivalente en Scala para declarar que el que llama del down realiza el downcast, de la misma forma que este tipo de tipo, pero para tipos generales. No creo que sea fácil, pero sería bueno.

Respuesta

7

[Nota: No estoy recomendando que usted hace esto.] Hay una feria posibilidad de que pueda lograr lo que quiere. El lanzamiento a this.type es una mentira, pero la JVM no lo sabe y no puede lanzar una excepción porque el tipo singleton es un concepto scala.

Ahora si realmente está utilizando la propiedad singleton de this.type en cualquier lugar, esto le causará problemas con prisa. Pero si todo lo que quiere hacer es conseguir tipos de retorno covariantes y sin la molestia de escribirlos, con sólo el pequeño inconveniente de la gran reparto feo todo el lugar:

trait Expression 
{ 
    def left : Expression 
    def right : Expression 

    def new_with_changes(l : Expression, r : Expression) : this.type 
} 

case class Derived1(left : Expression, right : Expression) extends Expression { 
    def new_with_changes(l : Expression, r : Expression) = 
    Derived1(left, right).asInstanceOf[this.type] 

    def foo() = "Derived1" 
} 

case class Derived2(left : Expression, right : Expression) extends Expression { 
    def new_with_changes(l : Expression, r : Expression) = 
    Derived2(left, right).asInstanceOf[this.type] 

    def bar() = "Derived2" 
} 

Y en acción:

scala> Derived1(Derived1(null,null), null) 
res0: Derived1 = Derived1(Derived1(null,null),null) 

scala> res0.new_with_changes(res0, null).bar 
<console>:6: error: value bar is not a member of Derived1 
     res0.new_with_changes(res0, null).bar 
             ^

scala> res0.new_with_changes(res0, null).foo 
res2: java.lang.String = Derived1 

scala> Derived2(Derived2(null, null), null) 
res3: Derived2 = Derived2(Derived2(null,null),null) 

scala> res3.new_with_changes(null, res3).foo 
<console>:6: error: value foo is not a member of Derived2 
     res3.new_with_changes(null, res3).foo 
             ^

scala> res3.new_with_changes(null, res3).bar 
res6: java.lang.String = Derived2 
+0

Hacky, pero funciona. Si pudiéramos obtener algo como esto en el lenguaje, eso sería genial. –

8

this.type es el único tipo de esta instancia en particular. Es un tipo singleton: un tipo distinto de cualquier otra instancia de la misma clase. Esto funciona

class Foo { def f : this.type = this} 

Pero esto no

class Foo { def f : this.type = new Foo} 

this.type no es necesario que a menudo, pero puede ser utilizado para expresar algunas limitaciones que no se pueden expresar de otro modo

Por ejemplo, aquí la clase interna dice que el método externo de cada instancia devolverá la instancia externa específica de la que proviene.

scala> class Outer{ class Inner { def outer : Outer.this.type = Outer.this}; def f(x : Inner) = println("ok")} 
defined class Outer 

scala> val o1 = new Outer 
o1: Outer = [email protected] 

scala> val o2 = new Outer 
o2: Outer = [email protected] 


scala> val in1 = new o1.Inner 
in1: o1.Inner = [email protected] 

scala> val in2 = new o2.Inner 
in2: o2.Inner = [email protected] 

scala> val o3 = in1.outer 
o3: o1.type = [email protected] 

scala> o1.f(new o3.Inner) 
ok 

scala> o1.f(new o2.Inner) 
<console>:8: error: type mismatch; 
found : o2.Inner 
required: o1.Inner 
     o1.f(new o2.Inner) 

Este artículo tiene otro buen ejemplo del uso de this.type para permitir método de encadenamiento a través de límites de subclases: http://scalada.blogspot.com/2008/02/thistype-for-chaining-method-calls.html

scala> class A { def method1: this.type = this } 
defined class A 

scala> class B extends A { def method2: this.type = this } 
defined class B 

scala> val b = new B 
b: B = [email protected] 

scala> b.method1.method2 
res3: b.type = [email protected] 
+2

Sería bueno tener una manera de hacer lo que me gustaría hacer, además de this.type, donde se podría declarar un método de clase base para devolver una instancia del subtipo que la persona que llama abatiría, de la misma manera que el código de llamada con this.type does. –

Cuestiones relacionadas