Javaプログラミング思想読書ノート——第九章:インタフェース


第九章インタフェース
  • インタフェースおよび実装クラス
  • 抽象クラスは普通のクラスとインタフェースの間に介在する中庸の道であり、抽象クラスも重要なツールであり、常に純粋なインタフェース
  • を使用することはできません.
    9.1抽象クラスと抽象メソッド
  • 抽象メソッド宣言の構文:
  • abstract void f();
  • クラスに1つ以上の抽象メソッドがある場合、クラスは抽象的に限定する必要があります.そうしないと、
  • とエラーが発生します.
  • abstractキーワードの最も重要な役割は、開発者に、どの方法が再定義されなければならないか、つまり
  • を書き換えることです.
  • はまた、このクラスを作成するオブジェクト
  • をブロックするなど、抽象的な方法のない抽象クラスを作成することもできる.
  • 直接見練習
  • 練習1、第8章練習9のRodentを修正し、抽象クラスにする
    //      ,      ,            
    abstract class Rodent {   
        public abstract void hop();   
        public abstract void scurry();   
        public abstract void reproduce(); 
    } 
     
    class Mouse extends Rodent {   
        public void hop() { print("Mouse hopping"); }   
        public void scurry() { print("Mouse scurrying"); }   
        public void reproduce() { print("Making more Mice"); }   
        public String toString() { return "Mouse"; } 
    } 

    練習2、抽象メソッドを含まない抽象クラスを作成し、クラスにオブジェクトを作成できないことを確認します.
    abstract class NoAbstractMethods {   
        void f() { System.out.println("f()"); } 
    } 
     
    public class E02_Abstract { 
        public static void main(String args[]) {     
            new NoAbstractMethods();   
        } 
    }
    //      ,            

    練習3、抽象メソッドprint()を含むベースクラスを作成し、エクスポートクラスでメソッドを上書きします.上書き後のメソッドバージョンでは、エクスポートクラスで定義された整数変数の値を印刷できます.変数を定義する場合は、ゼロ以外の値を指定します.このメソッドは、ベースクラスのコンストラクタで呼び出されます.mainメソッドで、クラスをエクスポートするオブジェクトを作成し、print()メソッドを呼び出します.発生した状況を説明してください.
    abstract class BaseWithPrint { 
      
        public BaseWithPrint() { 
            print(); 
        }   
    
        public abstract void print(); 
    } 
     
    class DerivedWithPrint extends BaseWithPrint {   
        int i = 47;   
        public void print() {     
            System.out.println("i = " + i);   
        } 
    } 
     
    public class E03_Initialization {   
        public static void main(String args[]) {     
            DerivedWithPrint dp = new DerivedWithPrint();     
            dp.print();   
        } 
    }
    //     
    i = 0 
    i = 47
    
    //            ,i       ,     0

    練習4、メソッドを含まない抽象クラスを作成し、そこからクラスをエクスポートし、メソッドを追加し、ベースクラスへの参照を受け入れ、エクスポートクラスに下に移行してから静的メソッドを呼び出す静的メソッドを作成します.main()で、その実行状況を示します.次に、ベースクラスのメソッドにabstract宣言を追加すると、ダウンコンバートは不要になります.
    abstract class NoMethods {} 
     
    class Extended1 extends NoMethods {   
        public void f() {     
            System.out.println("Extended1.f");   
        } 
    } 
     
    abstract class WithMethods {   
        abstract public void f(); 
    } 
     
    class Extended2 extends WithMethods {   
        public void f() {     
            System.out.println("Extended2.f");   
        } 
    } 
     
    public class E04_AbstractBase {   
        public static void test1(NoMethods nm) {     
            // Must downcast to access f():  
            ((Extended1)nm).f();   
        }   
        public static void test2(WithMethods wm) {     
            // No downcast necessary:     
            wm.f();   
        }   
        public static void main(String args[]) {     
            NoMethods nm = new Extended1();     
            test1(nm);     
            WithMethods wm = new Extended2();     
            test2(wm);   
        } 
    } 
    //     
    Extended1.f 
    Extended2.f
    
    //        ,        
    //        ,        
    //      ,             ,           ,      f()  ,      

    9.2インタフェース
  • interfaceは完全に抽象的なクラスを提供し、インタフェースは形式のみを提供し、具体的な実装は提供されず、インタフェースはクラスとクラス間のプロトコル
  • を確立するために使用される.
  • 以前はインタフェースでは変数を定義できないと考えられていたが、インタフェースにはドメインが含まれていてもよいが、暗黙的なstatic final
  • である.
    練習5、あるパッケージにインタフェースを作成し、3つの方法を含んで、それから別のパッケージでインタフェースを実現する
    //            

    練習6、証明インタフェース内の修正のすべての方法はpublicです
    //          public 
    //               public   

    練習7、8章の練習9を修正し、Rodentをインタフェースにする
    //           
    //       ,             ,      

    練習8、polymorphism.Sandwich.JAvaでは、インタフェースFastFoodを作成して適切なメソッドを追加し、Sandwichを変更してFastFoodインタフェースを実装します
    //         ,       

    練習9、再構築Music 5.JAvaは、Wind、Percussion、Stringedの共通メソッドを抽象クラスに移動します.
    //                    
    //              ,      

    練習10、修正Musci 5.JAvaは、Playableインタフェースを追加し、Play()の宣言をPlayableに移動し、Playableをimplementsリストに含めることでPlayableをエクスポートクラスに追加し、tune()を変更し、PlayableをインタフェースPlayableにする
    // Playable             

    9.3完全なデカップリング
  • インタフェースは、より多重性の良いコード
  • を記述できるようにすることができる.
  • は、伝達するパラメータオブジェクトによって異なる動作を有する方法を作成し、ポリシー設計モード
  • と称する.
  • インタフェースは継承と同じであり、いずれもマルチステート
  • がある.
  • mvpモードでは、インタフェースが存在する限り、インタフェースを宣言してオブジェクトを作成することができ、ある方法でこのインタフェースの参照も伝達することができ、最後にクラス
  • の実装に作用する.
  • 全体9.3は、インタフェースを使用すると、方法で参照して使用する場合など、インタフェースを使用していくつかの方法
  • を多重化できるなど、より柔軟になる可能性があることを示しています.
    //          :
    
    //   processor(   )  ,    :name()、process()
    //         ,Upcase、Downcase、Splitter
    //  Apply     process  :  processor     
    publc void process(Processor p, Object s)
    
    //         Filter(   )  ,            
    //         ,LowPass、HighPass、BandPass
    //       Apply  process  ,      Filter   
    
    //         ,          

    Stringタイプのパラメータを受け入れる方法があるクラスを作成し、その結果、そのパラメータの各ペアの文字を交換し、そのクラスを適切に適合させ、interfaceprocessorに使用できるようにする.Apply.process();
    //               
    public interface Processor {
        String name();
        Object process(Object input);
    }
    
    public class Apply {
        public static void process(Processor p, Object s) {
            print("Using Processor" + p.name());
            print(p.process(s));
        }
    }
    //       
    class CharacterPairSwapper {   
        static String swap(String s) {     
            StringBuilder sb = new StringBuilder(s);     
            for(int i = 0; i < sb.length() - 1; i += 2) {       
            char c1 = sb.charAt(i);       
            char c2 = sb.charAt(i + 1);       
            sb.setCharAt(i, c2);       
            sb.setCharAt(i + 1, c1);     
        }     
        return sb.toString();   
        } 
    } 
     
    class SwapperAdapter implements Processor {   
        public String name() {     
            return CharacterPairSwapper.class.getSimpleName();   
        }   
        public String process(Object input) {     
            return CharacterPairSwapper.swap((String)input);   
        } 
    } 
     
    public class E11_Swapper {   
        public static void main(String[] args) {     
            Apply.process(new SwapperAdapter(), "1234");     
            Apply.process(new SwapperAdapter(), "abcde");   
        } 
    } 
    //     
    Using Processor CharacterPairSwapper 
    2143 
    Using Processor CharacterPairSwapper 
    badce 
    
    
    //               ,             

    9.4 Javaでの多重継承
  • インタフェースは、より純粋な形式の抽象クラスだけでなく、これよりも高い目標を持っています.インタフェースには具体的な実装はまったくなく、いくつかのポートに関連するストレージ
  • はありません.
  • Javaでは多重継承は許可されていないが、複数のインタフェース
  • を実現することができる.
  • には例外があります.それはJavaが複数のインタフェースの継承を許可することです.以前の私はまだ知りませんでした.練習13は良い解釈です.
  • です.
    練習12、Adventure.JAvaでは、他のインタフェースのスタイルに従って、CanClimbインタフェースを追加します
    interface CanClimb {   
        void climb(); 
    } 
     
    class Hero2 extends ActionCharacter implements CanFight, CanSwim, CanFly, CanClimb {   
        public void swim() {}   
        public void fly() {}   
        public void climb() {} 
    }
    //       ,   Main            
    void z(CanClimb x) {
        x.climb();
    }

    練習13、1つのインタフェースを作成し、そのインタフェースから2つのインタフェースを継承し、後の2つのインタフェースから3つ目のインタフェースを多重継承する
    interface BaseInterface {   void f(); } 
     
    interface IntermediateInterface1 extends BaseInterface {   void f(); } 
     
    interface IntermediateInterface2 extends BaseInterface {   void f(); } 
     
    interface CombinedInterface extends IntermediateInterface1,IntermediateInterface2 {   
        void f(); 
    } 
     
    class CombinedImpl implements CombinedInterface {   
        public void f() {     
            System.out.println("CombinedImpl.f()");   
        }    
    } 
     
    public class E13_Diamond {   
        public static void main(String[] args) {     
            new CombinedImpl().f();   
        } 
    }
    //     
    CombinedImpl.f() 

    9.5継承によるインタフェースの拡張
    //       ,            
    interface Vampire extends DangerousMonster, Lethal {
    
    }

    練習14、3つのインタフェースを作成し、各インタフェースには2つの方法が含まれています.3つのインタフェースを組み合わせて新しいメソッドを追加するインタフェースが継承されます.インタフェースを実装し、特定のクラスを継承するクラスを作成します.4つのメソッドが作成され、各インタフェースの1つがパラメータとして受け入れられます.main()メソッドでは、このクラスのオブジェクトを作成し、4つのメソッドに渡します.
    interface Interface1 {   void f1();   void g1(); } 
     
    interface Interface2 {   void f2();   void g2(); } 
     
    interface Interface3 {   void f3();   void g3(); } 
     
    interface Multiple extends Interface1, Interface2, Interface3 {   void h(); } 
     
    class Concrete {   
        String s;   
        public Concrete(String s) { 
            this.s = s; 
        } 
    } 
     
    class All extends Concrete implements Multiple { 
        All() { super("All"); }   
        public void h() { print("All.h"); }   
        public void f1() { print("All.f1"); }   
        public void g1() { print("All.g1"); }   
        public void f2() { print("All.f2"); }   
        public void g2() { print("All.g2"); }   
        public void f3() { print("All.f3"); }   
        public void g3() { print("All.g3"); } } 
     
    public class E14_InterfaceInheritance {   
        static void takes1(Interface1 i) {     
            i.f1();     
            i.g1();   
        }   
        static void takes2(Interface2 i) {     
            i.f2();     
            i.g2();   
        }   
        static void takes3(Interface3 i) {     
            i.f3();     
            i.g3();   
        }   
        static void takesAll(All a) {     
            a.f1();     
            a.g1();     
            a.f2();     
            a.g2();     
            a.f3();     
            a.g3();     
            a.h();   
        }   
        public static void main(String args[]) {     
            All a = new All();     
            takes1(a);     
            takes2(a);     
            takes3(a);     
            takesAll(a);   
        } 
    } 
    //     
    All.f1 
    All.g1 
    All.f2 
    All.g2 
    All.f3 
    All.g3 
    All.f1 
    All.g1 
    All.f2 
    All.g2 
    All.f3 
    All.g3 
    All.h 
    
    //              

    エクササイズ15、前のエクササイズを、抽象クラスを作成し、エクスポートクラスに継承するように変更します.
    abstract class Abstract {   
        String s;   
        public Abstract(String s) { this.s = s; }   
        abstract void af(); 
    } 
     
    class All2 extends Abstract implements Multiple {   
        All2() { super("All2"); }   
        void af() { print("All.af"); }   
        public void f1() { print("All.f1"); }   
        public void g1() { print("All.g1"); }   
        public void f2() { print("All.f2"); }   
        public void g2() { print("All.g2"); }   
        public void f3() { print("All.f3"); }   
        public void g3() { print("All.g3"); }   
        public void h() { print("All2.h"); } 
    }
    
    //    Main       ,    ,new  All2            

    9.5.1インタフェースの結合時の名前の競合
    組み合わせるつもりの異なるインタフェースで同じメソッド名を使用すると、通常、コードの可読性が混乱し、できるだけ避けることができます.
    //          ,         :
    
    C  int f() I1  void f()        ,           
    class C5 extends C implements I1 {}
    //  
    I1  void f() I3  int f()        ,           
    interface I4 extends I1, I3 {}
    

    9.6適合コネクタ
  • インタフェースの最も魅力的な点は、同じインタフェースに複数の異なる具体的な実装
  • を許可することである.
    練習16、クラスを作成し、charシーケンスを生成し、このクラスに適合し、Scannerオブジェクトの入力となるようにする
    //                  
    // Scanner            Readable  
    //            Readable          read  
    //  Scanner            ,         
    

    9.7インタフェースのドメイン
  • インタフェースに入れるどのドメインも自動的にstatic finalであるため、インタフェースで定数グループを作成するのは便利な
  • である.
    練習17、インタフェース内のドメインがstaticとfinalであることを証明する
    interface StaticFinalTest {   
        String RED = "Red"; 
    }
    
    // main        OK  
    System.out.println("StaticFinalTest.RED = "StaticFinalTest.Red);

    9.7.1初期化インタフェースのドメイン
  • インタフェースで定義されたドメインは空finalではありませんが、
  • などの非常量式で初期化できます.
    public interface RandVals {
        Random RAND = new Random(47);
        int RANDOM_INT = RAND.newInt(10);
    }

    これらのドメインはインタフェースの一部ではなく、その値はインタフェースの静的記憶領域に格納される.
    9.8ネストされたインタフェース
  • インタフェースは、他のクラスまたは他のインタフェースにネストすることもできるし、対応するアクセス権
  • を有することもできる.
  • インタフェースを実装する場合、その内部にネストされたインタフェース
  • を実装する必要はない.
    9.9インタフェースと工場
  • あるインタフェースに従うオブジェクトを生成する典型的な方法は、ファクトリメソッド設計モードである:
  • //             getXXX  , new       
    //                  ,          
    
  • は、チェスとチェスのように、チェス工場とチェス共産がゲーム工場を継承している場合、それらは直接メソッドに呼び出され、どちらかというとマルチステートアプリケーションであり、この方法では異なるタイプでコード
  • を多重化することができます.
    void palyGame(GameFactory factory) {
    ...
    }

    練習18、CycleインタフェースとそのUnicycle、Bicycle、Tricycleインプリメンテーションを作成します.各タイプのCycleに対応するファクトリを作成し、コードを作成してこれらのファクトリを使用します.
    interface Cycle {   
        int wheels(); 
    } 
     
    interface CycleFactory {   
        Cycle getCycle(); 
    } 
     
    class Unicycle implements Cycle {   
        public int wheels() { 
            return 1; 
        } 
    } 
     
    class UnicycleFactory implements CycleFactory {   
        public Unicycle getCycle() { 
            return new Unicycle(); 
        } 
    } 
     
    class Bicycle implements Cycle {   
        public int wheels() { 
            return 2; 
        } 
    } 
     
    class BicycleFactory implements CycleFactory {   
        public Bicycle getCycle() { 
            return new Bicycle(); 
        } 
    } 
     
    class Tricycle implements Cycle {   
        public int wheels() { 
            return 3; 
        } 
    } 
     
    class TricycleFactory implements CycleFactory {   
        public Tricycle getCycle() { 
            return new Tricycle(); 
        } 
    } 
     
    public class E18_CycleFactories {   
    
        public static void ride(CycleFactory fact) {      
            Cycle c = fact.getCycle();      
            System.out.println("Num. of wheels: " + c.wheels());   
        }
       
        public static void main(String[] args) {     
            ride(new UnicycleFactory());     
            ride(new BicycleFactory ());     
            ride(new TricycleFactory ());   
        } 
    } 
    
    //     
    Num. of wheels: 1 
    Num. of wheels: 2 
    Num. of wheels: 3

    練習19、工場の方法を使って、コイン投げとサイコロ投げ機能を実行できるフレームワークを作成する
    interface Tossing { 
        boolean event(); 
    } 
     
    interface TossingFactory { 
        Tossing getTossing(); 
    }
    
    class CoinTossing implements Tossing {  
     
        private int events;   
        private static final int EVENTS = 2;   
        public boolean event() {     
            System.out.println("Coin tossing event " + events);     
            return ++events != EVENTS;   
        } 
    } 
     
    class CoinTossingFactory implements TossingFactory {  
     
        public CoinTossing getTossing() {     
            return new CoinTossing();   
        } 
    }
    
    class DiceTossing implements Tossing {   
    
        private int events;   
        private static final int EVENTS = 6;   
        public boolean event() {     
            System.out.println("Dice tossing event " + events);     
            return ++events != EVENTS;   
        } 
    } 
     
    class DiceTossingFactory implements TossingFactory { 
      
        public DiceTossing getTossing() {     
            return new DiceTossing();   
        } 
    }  
    
    public class E19_TossingFramework {
       
        public static void simulate(TossingFactory fact) {     
            Tossing t = fact.getTossing();     
            while(t.event())       
                ;   
        }
       
        public static void main(String[] args) {     
            simulate(new CoinTossingFactory());     
            simulate(new DiceTossingFactory());   
        } 
    }

    9.10まとめ
  • はインタフェースが理想的な選択であることを決定するので、いつもこの選択インタフェースは具体的なクラスではなく、これは誘惑であり、トラップであり、多くの人がトラップに落ち、可能であればインタフェースと工場を作成し、これは軽率な設計最適化
  • である.
    正しい方法:インタフェースではなくクラスを優先し、インタフェースの必須行が明確になったら再構築します.