読者です 読者をやめる 読者になる 読者になる

なみひらブログ

学んだことを日々記録する。~ since 2012/06/24 ~

Java8で、Objectが持つメソッドについてはインターフェースでdefault実装をできないようにしている理由

背景

Java8にて、インターフェースにdefaultメソッドメソッド定義と実装)を定義できるようになりました。

しかし、その実装においてObjectクラスが持っているメソッド(toString()とかequals()とか)は、インターフェース内でdefault実装として定義できないようになっています。(コンパイルエラー「A default method cannot override a method from java.lang.Object」)

「なんでかなぁ~」と思ってちょっと調べてみたら、以下の記事を見つけました。

ちょっと要約

ちょっと要約してみます。
#自分の解釈を一部入れています。
#英語に不自由のない方は上記を見てもらえればいいかと思います。

(質問者)

  • defaultメソッドは、Javaの中で新しい良いツールだ。
  • しかし、私がインターフェースにtoString()のdefault実装を定義しようとしたら、Javaは私に「これは禁止されている。Objectクラスで宣言されているメソッドは、default実装できないからだ。」と言ってきた。なぜだ?
  • 私は、「基底クラスが常に勝つ」というルールがあるということを知っている。そしてデフォルトでは(駄洒落)、Objectクラスが持つメソッドのどんなデフォルト実装もObjectクラス由来の実装によってとにかく上書きされてしまうだろう。しかしながら、Object由来のメソッドに関して例外が存在しない理由が私には分からない。
  • Java言語設計者が、Objectクラスのメソッドをdefalutメソッドでオーバーライドできないようにした理由は何だ?

この質問に対して、「Brian Goetz(@BrianGoetz)」というヒトが回答していて、このヒトについて調べてみると「Java Language Architect at Oracle」らしい。

(回答者)

  • 一見「良い設計」のようにみえる指摘だね。(あなたが「これが悪い設計」だと分かるまでは。。。ニヤリ)
  • 私たちを現在の設計に導いた幾つかの設計上の理由があるんだ。
    • 継承モデルをシンプルにし続けたい。
    • 「equals()/hashCode()/toString()を継承すること」は、「単一継承と(クラス)状態」などコンセプトに強く結びついているが、インターフェースは多重継承であり、状態も持たない。
  • あなたは既に"Keep it simple"という目的については言及している。継承と衝突解決のルールは、とてもシンプルに設計されている(クラスはインターフェースに勝つ。派生インターフェースは親インターフェースに勝つ。他のどんな衝突は、実装したクラスによって解決される)。
  • もちろんこれらのルールは例外を作るために調整することはできるだろう、しかし、それに手を出したときに、増大した複雑さはあなたが考えているほど小さくはないよ。正当化できる理由はつくれるけど、今回のケースはないよ。
  • 私たちがここで話しているメソッドはequls()、hashCode()、toString()。これらのメソッドは全て本来オブジェクトの状態についてのもの。それは状態をもっているクラスについてのものであり、インターフェースについてのものではないよ。これらのメソッドは、クラスに対して平等に評価するための最良の位置にあるんだ。インターフェースの書き手は、全く関係しない!
  • AbstractListの例を出すのが簡単だ。AbstractListを取り除きその振る舞いをListインターフェースに置き換えたら、それは良いことだろう。※前提として、AbstractListは単一継承ために設計されているが、インターフェースは多重継承のために設計されている。
  • あなたがこのようなクラスを書くことを考えてみよう。
class Foo implements com.libraryA.Bar, com.libraryB.Moo { 
    // Implementation of Foo, that does NOT override equals
}
  • Fooの書き手(あなた)は、親クラスを見てequals()の実装がないことがわかり、等値性としてはObjectクラスからequals()を継承するようにしている。
  • それから次の週、Barインターフェースに対するライブラリメンテナンスの際、”便利にするために”equals()のdefualt実装を追加する。うわ!今、”便利にするために”defaultメソッドが追加されたインターフェースによって、Fooクラスの意味(等値性)が壊されてしまった。
  • インターフェースにdefaultメソッドを追加することは、固く実装されているクラスの意味に影響を与えるべきではない。しかしもしdefaultメソッドによりObjectクラスのメソッドをオーバーライドできてしまったら、それは真実ではなくなるだろう。
  • だから、それは無害な機能追加のようにみえるけど、それは実際には非常に有害だ。それは、表現性を少し拡張できるけど、多くの複雑性を加わるし、別々にコンパイルされたインターフェースに対して無害に見える変更は、実装しているクラスの意図された意味(位置づけ)を弱体化してしまうんだ。

つまり

  • 継承モデルをシンプルにしたい。
    • (今の言語仕様「クラスが必ず勝つ」だと、インターフェースにObjectのメソッドをdefault実装しても呼ばれることはないんだけど、それを呼べるようにしたらルールが複雑になるという理解。)
  • Objectが持っているクラスはObject(~Class)の状態を表すためのメソッド群であり、それらはインターフェースとは関係がないし、そこで表現するべきではない。本来の住み分け大事。
  • もしインターフェースにて定義できたら、オブジェクトの状態/評価に大きな変更を加えてしまうことになる。

なかなか奥深い(´Д`)

補足

回答者は、Java8に関する質問にいろいろ答えてくれている。目を通しておきたい(´・ω・`)
User Brian Goetz - Stack Overflow

参考

interface - Java8: Why is it forbidden to define a default method for a method from java.lang.Object - Stack Overflow

Allow default methods to override Object's methods