Scalaのtype、気になるvisibility
同僚がC++でテンプレートごりごり使って書いたコードをScalaに移植しようとして、type(typedefみたいなアレ)周りで変なものを発見したと教えてくれたのが13日の金曜日。気になったので自分でもいじってみました。
# Scala version 2.9.2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_05)
class Foo[T] { val x = 10 type tx = T }
昔々、あるところにクラス Foo がありました。(ジェネリックな型であることは本題と関係なし)
class Bar1(foo: Foo[_]) { type tx = List[foo.tx] // NG }
Fooを引数に取るクラス。インスタンスの型に対応するコレクションを持つことにします。後々めんどくさいので別名をつけます。
error: private value foo escapes its defining scope as part of type java.util.List[Bar1.this.foo.tx]
何か文句を言われました。privateなどの不可視なものに触ったときのエラーは value x in class Hoge cannot be accessed in Hoge なので、状況が違いそう。
escapes defining scope...のエラーメッセージでぐぐってみると、Programming ScalaのChapter 5に同じエラーメッセージがあります。アクセス不可能なスコープにあるクラスを継承しようとした場合のエラーのようです。
Workaround
エラーメッセージは、privateなものをpublicにしているのが問題だと言っているように見えるので、その通り直せばコンパイルは通りました。
class Bar2(val foo: Foo[_]) { type tx = List[foo.tx] } class Bar3(foo: Foo[_]) { private type tx = List[foo.tx] }
ただし、Bar2はfooが外部に公開されているのでイマイチです。Bar3はtxがprivateになっているのでイマイチです。どちらも意味が変わってしまっています。
class Bar4(foo: Foo[_]) { private type tx = List[foo.tx] type public_tx = tx // NG }
List[foo.tx]を外側に出そうとしたらまた怒られました。
でも実は生のfoo.txだと大丈夫みたいで…
class Bar5(foo: Foo[_]) { private type tx = foo.tx type public_tx = tx } class Bar6(foo: Foo[_]) { private type tx = foo.tx type public_tx = List[tx] }
外側から触っても大丈夫のようです。
scala> val foo6 = new Bar6(new Foo[Int]) foo6: Bar6 = Bar6@243b31a6 scala> var x: bar6.public_tx = _ x: bar6.public_tx = null scala> val z = new HashMap[Int, bar6.public_tx] z: java.util.HashMap[Int,bar6.public_tx] = {}
コンパイルのためだけに一行増やすなんてどうかと思いつつも、とりあえず解決。いいのかなあこれで。
objectの中にtypeを定義した場合は、好き勝手にアクセスできるpublicなイメージなので、なんだかもやもやします。