2009-04-22 7 views
13

this.typeを使用して、不変のケースクラスの新しいインスタンスを作成するメソッドを定義したいと考えています。このような何か: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) 
    } 
} 

は残念ながら、コンパイラは新しいケースクラスはthis.typeと一致していませんどのように来る

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

を不平を言いますか?

Base.new_with_changesでthis.typeをBaseに変更し、Derived.new_with_changesで派生しても動作しますが、this.typeがきれいでないと思われます。

編集:この問題の本来の目的は、downの呼び出し側がthis.typeと同じようにダウンキャストを実行することを宣言するために、Scalaで同等の方法を使用しない理由です。私はそれが簡単だとは思わないが、それはいいだろう。

答えて

7

[注意:私はあなたがこれを行うことを推奨しておりません]フェアがありますあなたが望むものを達成できるチャンス。 this.typeへのキャストは嘘ですが、シングルトンタイプはスカラのコンセプトなので、JVMはそれを認識せず、例外をスローできません。

ここで実際にthis.typeのシングルトンプロパティを使用している場合、これは急いで問題になるでしょう。しかし、あなたがしたいすべてが、すべての場所の上に巨大な醜いキャストのほんの欠点で、それらを入力して、すべてのトラブルもなく共変戻り値の型を取得している場合:

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

とアクションで:

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

ハッキーですが、うまくいきます。私たちがこのようなことを言語に与えることができれば、それはクールだ。 –

8

this.typeは、この特定のインスタンスの一意のタイプです。これはシングルトンタイプです - 同じクラスの他のインスタンスとは異なるタイプです。これは

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

に動作します。しかし、これは

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

this.typeは、その多くの場合、必要とされていないませんが、そう

表現することができない、いくつかの制約を表現するために使用することができます例えば、Innerクラスでは、各インスタンスの外部メソッドは、それが来た特定の外部インスタンスを返します。

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) 

この記事は、サブクラスの境界を越えて連鎖する方法を可能にするためにthis.typeを使用して別の良い例があります。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

this.type以外に、私がやりたいことをやり遂げる方法があるといいでしょう。そこでは、ベースクラスのメソッドが、呼び出し元がダウンキャストするサブタイプのインスタンスを返すよう宣言できますthis.typeを使用した呼び出し元コードは行いません。 –

関連する問題