web-dev-qa-db-ja.com

Java8:Java.lang.Objectのメソッドのデフォルトメソッドを定義することが禁止されている理由

デフォルトのメソッドは、Javaツールボックスの新しいツールです。ただし、defaultメソッドのtoStringバージョンを定義するインターフェイスを記述しようとしました。 Javaは、Java.lang.Objectdefaultedにはできません。これはなぜですか?

「基本クラスは常に勝つ」というルールがあることを知っているので、デフォルトでは(pun;)、defaultメソッドのObject実装はObjectとにかく。ただし、仕様のObjectからのメソッドの例外が存在しない理由はありません。特にtoStringの場合、デフォルトの実装を使用すると非常に便利です。

だから、JavaデザイナーがdefaultメソッドをObjectからオーバーライドすることを許可しないことに決めた理由は何ですか?

117
gexicide

これは言語設計の問題のもう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のセマンティクスは、別のメンテナンスドメインのインターフェイスによって、一般的なメソッドのデフォルトを「役立つように」追加することで壊れています。

デフォルトはデフォルトになるはずです。階層のどこにも存在しないインターフェイスにデフォルトを追加しても、具体的な実装クラスのセマンティクスに影響はありません。しかし、デフォルトがオブジェクトメソッドを「オーバーライド」できる場合、それは真実ではありません。

したがって、それは無害な機能のように見えますが、実際には非常に有害です:それは少し増分表現するために多くの複雑さを追加し、意図的に、無害に見える変更が別々にコンパイルされたインターフェースを非常に簡単に損なうようにします実装クラスの意図されたセマンティクス。

171
Brian Goetz

Java.lang.Objectのメソッドのインターフェースでデフォルトのメソッドを定義することは禁止されています。デフォルトのメソッドは決して「到達可能」ではないからです。

デフォルトのインターフェイスメソッドは、インターフェイスを実装するクラスで上書きできます。メソッドがスーパークラスで実装されている場合でも、メソッドのクラス実装はインターフェイス実装よりも優先されます。すべてのクラスはJava.lang.Objectを継承するため、Java.lang.Objectのメソッドはインターフェイスのデフォルトメソッドよりも優先され、代わりに呼び出されます。

OracleのBrian Goetzが、この メーリングリストの投稿 で設計決定に関する詳細をいくつか提供しています。

29
jarnbjo

Java言語の作者の頭には見えないので、推測するだけかもしれません。しかし、私は多くの理由を見て、この問題で絶対に同意します。

デフォルトのメソッドを導入する主な理由は、古い実装の後方互換性を損なうことなく、インターフェイスに新しいメソッドを追加できるようにするためです。デフォルトのメソッドを使用して、実装クラスのそれぞれでそれらを定義する必要なしに「便利な」メソッドを提供することもできます。

これらは、toStringおよびObjectの他のメソッドには適用されません。簡単に言えば、デフォルトのメソッドは、他に定義がないdefaultの動作を提供するように設計されています。他の既存の実装と「競合する」実装を提供しない。

「基本クラスが常に勝つ」というルールにも確固たる理由があります。クラスはreal実装を定義し、インターフェースはdefault実装を定義すると想定されています、やや弱いです。

また、一般的な規則に例外を導入すると、不必要な複雑さが生じ、他の疑問が生じます。オブジェクトは(多かれ少なかれ)他と同じクラスですが、なぜ異なる振る舞いを持つべきなのでしょうか?

結局のところ、あなたが提案する解決策はおそらくプロよりも多くの短所をもたらすでしょう。

3
Marwin

理由は非常に単純です。なぜなら、ObjectはすべてのJavaクラスの基本クラスであるためです。インターフェイスのデフォルトメソッドとしてObjectのメソッドが定義されている場合でも、Objectメソッドは常に使用されるため、混乱を避けるために、Objectクラスのメソッドをオーバーライドするデフォルトのメソッドを使用することはできません。

1
Kumar Abhishek

非常につまらない答えを出すために、_Java.lang.Object_のpublicメソッドに対してdefaultメソッドを定義することは禁止されています。考慮すべき11の方法があり、この質問に答えるために3つの方法に分類できます。

  1. 6つのObjectメソッドは、defaultであり、まったくオーバーライドできないため、finalメソッドを持つことはできません:getClass()notify()notifyAll()wait()wait(long)、およびwait(long, int)
  2. 3つのObjectメソッドは、上記のブライアンゲーツによる理由により、defaultメソッドを持つことができません:equals(Object)hashCode()、およびtoString()
  3. 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();
            }
        }
    }
    _
1
jaco0646