2009-04-22 5 views
13

Ich möchte in der Lage seinen this.type zu verwenden, um eine Methode zu definieren, die neuen Instanzen einer unveränderlichen Fall Klasse. Etwas wie folgt aus:Warum kann nicht für neue Instanzen verwendet werden this.type

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

Leider ist der Compiler beschwert

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

Wie kommt der neue Fall Klasse this.type nicht überein?

Wenn ich this.type zu Base in Base.new_with_changes ändert und Abgeleitet in Derived.new_with_changes das funktioniert, aber es scheint, es fehlt auf der Seite dieses Typs.

Edit: Die wahre Absicht der Frage ist, warum nicht eine äquivalente Art in Scala zu deklarieren, dass der Aufrufer der down die downcast durchführen, sehr ähnlich wie this.type, aber für allgemeine Typen. Ich denke nicht, dass es einfach wäre, aber es wäre nett.

Antwort

7

[Anmerkung: Ich empfehle nicht, dass Sie dies tun.] Es ist eine faire Chance, dass du erreichen kannst, was du willst. Die Umwandlung in diesen Typ ist eine Lüge, aber die JVM weiß das nicht und kann keine Ausnahme auslösen, weil der Singletontyp ein Skalakonzept ist.

Nun, wenn Sie tatsächlich die Singleton-Eigenschaft this.type überall verwenden, dies wird Sie in Schwierigkeiten in Eile. Aber wenn alles, was Sie tun möchten, ist kovariante Rückgabetypen ohne all die Schwierigkeiten von ihnen eingeben, nur mit dem kleinen Nachteil der großen hässlichen Besetzung der ganzen Ort:

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

Und in Aktion:

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, aber es funktioniert. Wenn wir so etwas in die Sprache bringen könnten, wäre das cool. –

8

this.type ist der eindeutige Typ dieser bestimmten Instanz. Es ist ein Singleton-Typ - ein eindeutiger Typ von jeder anderen Instanz derselben Klasse. Dies funktioniert

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

Aber dies bedeutet nicht

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

this.type nicht so oft gebraucht wird, aber es kann verwendet werden, um einige Einschränkungen ausdrücken, die nicht auf andere Weise ausgedrückt werden kann

Zum Beispiel sagt die innere Klasse hier, dass die äußere Methode jeder Instanz die spezifische äußere Instanz zurückgibt, von der sie gekommen ist.

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) 

Dieser Artikel hat ein weiteres schönes Beispiel this.type der Verwendung von Verfahren zu ermöglichen, über Unterklasse Grenzen Verkettungs: 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

Es wäre schön, einen Weg zu haben, etwas zu tun ich, neben this.type tun möchte, wo eine Basisklasse Methode deklariert werden könnte eine Instanz des Subtyps zurückzugeben, die der Anrufer würde, auf die gleiche Art und Weise, dass die niedergeschlagenen Anrufercode mit this.type tut das. –