String と java.lang.String は同じものなのか?

この記事は不正確です。無かったことにしてください。m(_ _)m

 Scala 2.7.5 で検証。

 Scala で以下のようなトレイトがあったとします。

trait Hoge {
  def foo: String
}

 それで、以下のような実装をすると怒られます。

// コンパイルが通らない
class Fuga extends Hoge {
  override def foo = (new java.lang.Date()).toString
}

 理由は、これはオーバーライドの関係が成り立たないかららしいです。つまり、Hoge トレイトの foo メソッドの戻り値の型と Fuga クラスの foo メソッドの戻り値の型は別物であるとされるわけです。

 これを解決するには以下のようにします。

class Fuga extends Hoge {
  override def foo: String = (new java.lang.Date()).toString
}

 戻り値の型を明示しただけでうまくいってしまうんですね。

 ちなみに String は Predef で定義されている java.lang.String の別名のようです。しかし、ここで疑問が。

  • そもそもなんで別名定義してるのか?
    • java.lang 配下のクラスは Scala であっても import なしで使えるはず
    • 単なる整合性(見た目?)の問題?
  • なぜ明示しないと動かないのか?
    • 逆になぜ明示すれば動くのか

 そこで一つの予想(妄想)が出てくるわけです。それは String は将来 java.lang.String とは別物にするか、別名定義ではない方法で java.lang.String を再定義するのでは?ということです。

 そうすると、下手にキャストするのはまずいのではないかとまで思えてくるわけです。たとえば、さきほどの Fuga クラスは以下のようにもかけるわけですが、これはまずいと。

class Fuga extends Hoge {
  override def foo = (new java.lang.Date()).toString.asInstanceOf[String]
}

なぜまずいかというと前者は、キャストしているかどうかはわかりませんが、とにかくコンパイラの処理にお任せしているのに対し、後者は何が何でもキャストしようとしているわけです。将来 String と java.lang.String の間に代入互換性が仮になくなったとしても、コンパイラにこのへんをお任せしていればなんとかしてくれそうですが、キャストはそうも行かないだろうというのが私の妄想に近い想像です。

ということで、Java 型と Scala 型の変換は可能な限りコンパイラにお任せしようと思う今日この頃です。