trait A { type Self <: A }class B extends A { type Self = B }ってよくやるけどもっといい方法はあるのだろうか。
— ねこはるさん (@halcat0x15a) 2月 17, 2012
に対し、りりかるろじかるさんが反応をくれたので書いてみます。
最初に、これが何の役に立つかですが、
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
trait Foo { | |
type Self <: Foo | |
val i: Int | |
def f(i: Int): Self | |
def g(i: Int) = Option(f(i)) | |
} | |
case class Bar(i: Int) extends Foo { | |
type Self = Bar | |
def f(i: Int) = Bar(i) | |
def h = "hoge" | |
} |
と定義したとき、
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
val bar: Bar = Bar(100) | |
assert(bar == Bar(100)) | |
val foo: Bar = bar.f(10) | |
assert(foo == Bar(10)) | |
val foobar: Option[Bar] = foo.g(1) | |
assert(foobar == Option(Bar(1))) | |
val baz: String = foo.h | |
assert(baz == "hoge") |
となります。
ポイントは、
- Barからfを呼び出すとBarが返る。
- Barからgを呼び出すとOption[Bar]が返る。
- fの返り値からhを呼び出せる。
つまり、スーパークラスでサブクラスの値を返すことができるということです。
しかし、これにはサブクラスがいちいちSelfを定義しなければいけないので楽したいですよね。
そこで、りりろじさんに考えて頂いた方法を書いていきます。
this.type
this.typeを使って定義すると、
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
trait Foo { | |
val i: Int | |
def f(i: Int): this.type | |
def g(i: Int) = Option(f(i)) | |
} | |
case class Bar(i: Int) extends Foo { | |
type Self = Bar | |
def f(i: Int) = Bar(i).asInstanceOf[this.type] | |
def h = "hoge" | |
} |
となります。
Bar.this.typeが要求されているところでBarを渡すとコンパイルエラーがでます。
なので、asInstanceOfを使って定義します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
val bar: Bar = Bar(100) | |
assert(bar == Bar(100)) | |
val foo: Bar = bar.f(10) | |
assert(foo == Bar(10)) | |
/* | |
* scala> val foobar: Option[Bar] = foo.g(1) | |
* <console>:12: error: type mismatch; | |
* found : Option[Foo] | |
* required: Option[Bar] | |
* val foobar: Option[Bar] = foo.g(1) | |
* ^ | |
*/ | |
val baz: String = foo.h | |
assert(baz == "hoge") |
なんかエラー出てる・・・・
うーん、これはどういうことでしょう?
gの返り値をOption[this.type]と明示的に書いても変わりません。
むむむむむ・・・・・えらいひと、教えて下さい。
型パラメータ
Genericsまんせー
ということで書いてみる。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
trait Foo[A <: Foo[_]] { | |
val i: Int | |
def f(i: Int): A | |
def g(i: Int) = Option(f(i)) | |
} | |
case class Bar(i: Int) extends Foo[Bar] { | |
def f(i: Int) = Bar(i) | |
def h = "hoge" | |
} | |
val bar: Bar = Bar(100) | |
assert(bar == Bar(100)) | |
val foo: Bar = bar.f(10) | |
assert(foo == Bar(10)) | |
val foobar: Option[Bar] = foo.g(1) | |
assert(foobar == Option(Bar(1))) | |
val baz: String = foo.h | |
assert(baz == "hoge") |
おお、ちゃんと動きますね。
この型パラメータ版とtype Self版で比べてみると、
- 型パラメータ有無
- どちらも、型の情報がなくなると意味がない
- 継承元がtype Selfを書くか、型パラメータに型を書くか
型パラメータを使ったほうが楽に書けそうですが、type Selfを使うと楽になることもあります。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
trait Foo { | |
type Self <: Foo | |
val i: Int | |
def f(i: Int): Self | |
} | |
trait Baz { self: Foo => | |
def g(i: Int) = Option(f(i)) | |
} | |
case class Bar(i: Int) extends Foo with Baz { | |
type Self = Bar | |
def f(i: Int) = Bar(i) | |
} |
このように、複数のtraitでSelfを使う場合はこの書き方が有利なようです。
他にもっといい方法があったら是非教えてくださいねー!
0 件のコメント:
コメントを投稿