デフォルトのメソッドは、Javaツールボックスの新しいツールです。ただし、default
メソッドのtoString
バージョンを定義するインターフェイスを記述しようとしました。 Javaは、Java.lang.Object
はdefault
edにはできません。これはなぜですか?
「基本クラスは常に勝つ」というルールがあることを知っているので、デフォルトでは(pun;)、default
メソッドのObject
実装はObject
とにかく。ただし、仕様のObject
からのメソッドの例外が存在しない理由はありません。特にtoString
の場合、デフォルトの実装を使用すると非常に便利です。
だから、Javaデザイナーがdefault
メソッドをObject
からオーバーライドすることを許可しないことに決めた理由は何ですか?
これは言語設計の問題のもう1つであり、掘り出し始めて実際に悪い考えであることに気付くまでは「明らかに良い考え」です。
このメール 主題(および他の主題にも)がたくさんあります。現在の設計に導くために収束した設計力がいくつかありました。
AbstractList
をインターフェイスに変える)、equals/hashCode/toStringの継承は単一の継承と状態に強く結び付けられ、インターフェイスは多重に継承され、ステートレスになります。 ;「シンプルに保つ」という目標については既に触れました。継承と競合解決のルールは非常にシンプルになるように設計されています(クラスがインターフェースに勝ち、派生インターフェースがスーパーインターフェースに勝ち、その他の競合は実装クラスによって解決されます)。もちろん、これらのルールを調整して例外を作成することもできますが、その文字列を引き出し始めると、増分の複雑さは思ったほど小さくないことがわかると思います。
もちろん、より複雑なことを正当化するある程度の利点がありますが、この場合はありません。ここで説明しているメソッドは、equals、hashCode、およびtoStringです。これらのメソッドはすべて本質的にオブジェクトの状態に関するものであり、インターフェースではなく状態を所有するクラスが、クラスの平等の意味を判断するのに最適な位置にいます(特に、平等の契約は非常に強力です。 Java);インターフェースライターはあまりにも削除されています。
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
実装を追加します。おっと! Foo
のセマンティクスは、別のメンテナンスドメインのインターフェイスによって、一般的なメソッドのデフォルトを「役立つように」追加することで壊れています。
デフォルトはデフォルトになるはずです。階層のどこにも存在しないインターフェイスにデフォルトを追加しても、具体的な実装クラスのセマンティクスに影響はありません。しかし、デフォルトがオブジェクトメソッドを「オーバーライド」できる場合、それは真実ではありません。
したがって、それは無害な機能のように見えますが、実際には非常に有害です:それは少し増分表現するために多くの複雑さを追加し、意図的に、無害に見える変更が別々にコンパイルされたインターフェースを非常に簡単に損なうようにします実装クラスの意図されたセマンティクス。
Java.lang.Object
のメソッドのインターフェースでデフォルトのメソッドを定義することは禁止されています。デフォルトのメソッドは決して「到達可能」ではないからです。
デフォルトのインターフェイスメソッドは、インターフェイスを実装するクラスで上書きできます。メソッドがスーパークラスで実装されている場合でも、メソッドのクラス実装はインターフェイス実装よりも優先されます。すべてのクラスはJava.lang.Object
を継承するため、Java.lang.Object
のメソッドはインターフェイスのデフォルトメソッドよりも優先され、代わりに呼び出されます。
OracleのBrian Goetzが、この メーリングリストの投稿 で設計決定に関する詳細をいくつか提供しています。
Java言語の作者の頭には見えないので、推測するだけかもしれません。しかし、私は多くの理由を見て、この問題で絶対に同意します。
デフォルトのメソッドを導入する主な理由は、古い実装の後方互換性を損なうことなく、インターフェイスに新しいメソッドを追加できるようにするためです。デフォルトのメソッドを使用して、実装クラスのそれぞれでそれらを定義する必要なしに「便利な」メソッドを提供することもできます。
これらは、toStringおよびObjectの他のメソッドには適用されません。簡単に言えば、デフォルトのメソッドは、他に定義がないdefaultの動作を提供するように設計されています。他の既存の実装と「競合する」実装を提供しない。
「基本クラスが常に勝つ」というルールにも確固たる理由があります。クラスはreal実装を定義し、インターフェースはdefault実装を定義すると想定されています、やや弱いです。
また、一般的な規則に例外を導入すると、不必要な複雑さが生じ、他の疑問が生じます。オブジェクトは(多かれ少なかれ)他と同じクラスですが、なぜ異なる振る舞いを持つべきなのでしょうか?
結局のところ、あなたが提案する解決策はおそらくプロよりも多くの短所をもたらすでしょう。
理由は非常に単純です。なぜなら、ObjectはすべてのJavaクラスの基本クラスであるためです。インターフェイスのデフォルトメソッドとしてObjectのメソッドが定義されている場合でも、Objectメソッドは常に使用されるため、混乱を避けるために、Objectクラスのメソッドをオーバーライドするデフォルトのメソッドを使用することはできません。
非常につまらない答えを出すために、_Java.lang.Object
_のpublicメソッドに対してdefault
メソッドを定義することは禁止されています。考慮すべき11の方法があり、この質問に答えるために3つの方法に分類できます。
Object
メソッドは、default
であり、まったくオーバーライドできないため、final
メソッドを持つことはできません:getClass()
、notify()
、notifyAll()
、wait()
、wait(long)
、およびwait(long, int)
。Object
メソッドは、上記のブライアンゲーツによる理由により、default
メソッドを持つことができません:equals(Object)
、hashCode()
、およびtoString()
。Object
メソッドの2つcanにはdefault
メソッドがありますが、そのようなデフォルトの値はせいぜい疑わしいです:clone()
とfinalize()
。
_public class Main {
public static void main(String... args) {
new FOO().clone();
new FOO().finalize();
}
interface ClonerFinalizer {
default Object clone() {System.out.println("default clone"); return this;}
default void finalize() {System.out.println("default finalize");}
}
static class FOO implements ClonerFinalizer {
@Override
public Object clone() {
return ClonerFinalizer.super.clone();
}
@Override
public void finalize() {
ClonerFinalizer.super.finalize();
}
}
}
_