2009-04-22 5 views
13

J'aimerais pouvoir utiliser this.type pour définir une méthode qui crée de nouvelles instances d'une classe de cas immutable. Quelque chose comme ceci:Pourquoi ce type.type ne peut-il pas être utilisé pour les nouvelles instances?

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

Malheureusement, le compilateur se plaint

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

Comment se fait la nouvelle classe de cas ne correspond pas à this.type?

Si je change this.type à Base dans Base.new_with_changes et Dérivé dans Derived.new_with_changes cela fonctionne, mais il semble qu'il manque sur la finesse de this.type. Edit: Le but réel de la question est pourquoi ne pas avoir un moyen équivalent dans Scala de déclarer que l'appelant de down exécute la descente, un peu de la même manière que this.type, mais pour les types généraux. Je ne pense pas que ce serait facile, mais ce serait bien.

Répondre

7

[Note: Je ne recommande pas que vous faites cela.] Il y a une foire chance que vous pouvez accomplir ce que vous voulez. Le cast à this.type est un mensonge, mais la JVM ne le sait pas et ne peut pas lancer une exception car le type singleton est un concept de scala. Maintenant, si vous utilisez réellement la propriété singleton de this.type n'importe où, cela vous donnera des problèmes rapidement. Mais si tout ce que vous voulez faire est d'obtenir les types de retour covariants sans tous la peine de les taper, avec seulement le petit inconvénient de l'énorme distribution laid partout:

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

Et en action:

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, mais ça marche. Si nous pouvions obtenir quelque chose comme ça dans la langue, ce serait cool. –

8

this.type est le type unique de cette instance particulière. C'est un type singleton - un type distinct de n'importe quelle autre instance de la même classe. Cela fonctionne

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

Mais cela ne

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

this.type n'est pas nécessaire que souvent, mais il peut être utilisé pour exprimer certaines contraintes qui ne peuvent être exprimées autrement

Par exemple, ici, la classe Inner indique que la méthode externe de chaque instance retournera l'instance externe spécifique dont elle provient.

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) 

Cet article a un autre bel exemple d'utiliser this.type pour activer la méthode enchaînant à travers les frontières de la sous-classe: 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

Ce serait bien d'avoir une façon de faire ce que je voudrais faire, en plus de this.type, où une méthode de classe de base pourrait être déclarée pour renvoyer une instance de sous-type que l'appelant abattrait, de la même manière que le code de l'appelant avec this.type fait. –

Questions connexes