Java 8のインターフェースにおけるデフォルトの方法と静的な方法

13394 ワード

転載は出所を明記してください.http://blog.csdn.net/airsaid/article/details/51017534 本論は「周遊ブログ」です.
  • クライテリア
  • デフォルトの方法
  • デフォルト方法の定義
  • デフォルトの方法の使用
  • デフォルト方法の継承
  • どうしてデフォルトの方法がありますか?
  • デフォルト方法による問題
  • デフォルト方法まとめ
  • 静的方法
  • 前言
    Java 8では、インターフェースはいくつかの新しい言語特性を導入している:デフォルトの方法(Default Methods)および静的な方法(Static Methods).この文章はこの二つの特性を理解します.
    デフォルトの方法
    デフォルトの方法の定義
    デフォルトの方法の定義はdefaultキーワードを使用する必要があります.例えば、
    public interface A {
        default String getName(){
            return "A";
        }
    }
    デフォルトの方法はprvateに修飾されてはいけません.デフォルトはpublicです.publicは省略して書かなくてもいいです.
    デフォルトの方法の使用
    このインターフェースが実装されたクラスがある場合、このデフォルトの方法は、例えば、
    public class C implements A{
    
    }
    public class Main{
        public static void main(String[] args) {
            System.out.println(new C().getName());// A
        }
    }
    また、具体的なインターフェース実装クラスでは、インターフェースにおけるデフォルトの方法を上書きすることもできます.たとえば:
    public class C implements A{
        @Override
        public String getName() {
            return "C";
        }
    }
    public class Main{
        public static void main(String[] args) {
            System.out.println(new C().getName());// C
        }
    }
    デフォルトの方法の継承
    みんな知っています.インターフェースとインターフェースは継承できます.デフォルトの方法がある場合、インターフェースの継承はどうなりますか?次の継承例を見てみましょう.
    public interface A {
        default String getName(){
            return "A";
        }
    }
    public interface B extends A{
    
    }
    public interface C extends B{
        @Override
        default String getName() {
            return "C";
        }
    }
    public interface D extends C{
        @Override
        String getName();
    }
    public class Main{
        public static void main(String[] args) {
            System.out.println(new A(){}.getName());// A
            System.out.println(new B(){}.getName());// A
            System.out.println(new C(){}.getName());// C
            System.out.println(new D(){
                @Override
                public String getName() {
                    return "D";
                }
            }.getName());// D
        }
    }
    結果を印刷することによって、継承関係にはこのようないくつかの状況があることが分かります.
  • は親インターフェースのデフォルト方法を覆さず、直接親インターフェースの方法で実現します.(Bインターフェースを参照)
  • 親インターフェースのデフォルトの方法を上書きして、自分で実現します.(Cインターフェースを参照)
  • 親インターフェースのデフォルトの方法を繰り返して実装し、抽象的な方法に変更します.(Dインターフェースを参照)
  • 前の二つがよく分かります.類の継承と同じです.三つ目は、デフォルトの方法を抽象的な方法に変えると、実現類はこの抽象的な方法を実現しなければなりません.
    なぜデフォルトの方法がありますか?
    一つのシーンを想像して、デフォルトの方法がない場合、集合フレームに新しい反復機能を追加する必要があります.Iterableインターフェースに新しい抽象的な方法を追加する必要がありますが、その結果、インターフェースの下の実現クラスはこの方法を実現しなければなりません.しかし、集合フレームシステムの巨大さのために、実现クラスは非常に多くあります.
    しかし、デフォルトの方法があると、この問題を解決しました.インターフェースに新しいデフォルトの方法が追加されても、具体的な実現クラスは変更する必要がなく、新しい機能があります.
    デフォルトの方法の出現は、既存のコードの互換性を確保しつつ、インターフェースに新たな機能を追加することです.
    デフォルトの方法による問題
    デフォルトの方法はいいですが、私たちも濫用してはいけません.デフォルトの方法は複雑な継承関係の中で私達のプログラムに曖昧さとコンパイルミスをもたらすからです.
    インターフェースは多く継承されますので、次のような状況を見てみます.
    public interface A {
        default String getName(){
            return "A";
        }
    }
    public interface B {
        default String getName(){
            return "B";
        }
    }
    public class C implements A, B{
    
    }
    CクラスがA、Bインターフェースを同時に実現する場合、コンパイルは通過できません.明らかに、A、Bインターフェースにはget Name()の方法がありますので、もしコンパイルが通れば、どのようにプログラムを呼び出すか分かりますか?このような場合、C類はget Name()の方法を上書きして、自分のほしい「結果」を与えなければならない.例えば、Aの「結果」を使う.
    @Override
    public String getName() {
        return A.super.getName();
    }
    あるいは自分で「結果」をあげます.
    @Override
    public String getName() {
        return "C";
    }
    最終的に呼び出した場合、印刷結果はA、Cです.
    もう一つのちょっと複雑な状況があります.この例を見てみます.
    public interface A {
        default String getName(){
            return "A";
        }
    }
    public interface B extends A{
        @Override
        default String getName() {
            return "B";
        }
    }
    public class Main{
    
        static class C implements A, B{
    
        }
    
        static class D implements A, B{
            @Override
            public String getName() {
    //            return A.super.getName();//   
                return B.super.getName();//   
            }
        }
    
        public static void main(String[] args) {
            System.out.println(new C().getName());// B
            System.out.println(new D().getName());// B
        }
    }
    Cという種類は同時にA、Bインターフェースを実現しました.どうして間違った報告をしないで、get Name()方法を実現させましたか?よく見ると、BとAは継承の関係です.BがAを継承した後にgetName()の方法を書いて、それではAのgetName()は実はすでに遮られました.この二つのインターフェースを同時に実現すると、プリントされたのはもちろんBの「結果」です.同じ理由で、DクラスではAインターフェースの実装ができないのも同じです.
    これは、インターフェース継承挙動が衝突するときのルールの一つであり、他のタイプによってカバーされる方法は無視される.
    しかし、私たちはAインターフェースの結果がほしいです.どうすればいいですか?この問題を解決するためにインターフェースを新たに定義することができます.
    public interface A {
        default String getName(){
            return "A";
        }
    }
    public interface B extends A{
        @Override
        default String getName() {
            return "B";
        }
    }
    public interface C extends A{
        @Override
        default String getName() {
            return A.super.getName();//   ,        A      
        }
    }
    public class Main{
    
        static class D implements B, C{
            @Override
            public String getName() {
                return C.super.getName();
            }
        }
    
        public static void main(String[] args) {
            System.out.println(new D().getName());// A
        }
    }
    一周回って、やっとAの結果が出ました.この例では、新たに定義されたインターフェースでは、Aインターフェースの実装を呼び出す必要がありますが、起動しないと、AインターフェースのgetName()方法は依然としてBインターフェースによって上書きされて遮蔽されます.ぜひ覚えてください
    デフォルトの方法のまとめ
    デフォルトの方法は私達にインターフェースを修正し、既存の実現類を破壊しないために便利です.しかし、同時にいくつかの問題をもたらしました.例えば、上記で述べたように、相続衝突があります.及びインターフェースとクラスの関係は制約及び実現によってより曖昧になる.ですから、デフォルトの方法を定義する前に、本当にしなければならないのかを考えなければなりません.
    静的方法
    Java 8から開始して、インターフェースは標準的な方法を定義することができるだけでなく、静的な方法を定義することもできる.
    インターフェース内の静的方法の定義とクラス内の定義は同じです.
    public interface A {
        static void print(){
            System.out.println("A");
        };   
    }
    ただしインターフェースでは、デフォルトはpublicによって修飾され、デフォルトの方法と同様にprvateに変更することはできません.
    呼び出し時、直接インターフェース名.メソッド名:
    A.print();