Javaの内部クラスとその作成方法

37883 ワード

内部クラスとその作成方法
引用する
クラスを定義するときに、あるクラスを別のクラスの内部に定義することができます.このクラスは内部クラスになります.
内部クラスは、論理的に関連するクラスを整理し、内部にあるクラスの可視性を制御できるため、非常に有用な特性です.しかし,内部クラスと組合せは2つの全く異なる地方概念であることを理解することが重要である.
最初は、内部クラスがhiをコードの隠蔽メカニズムとして考えているように見えますが、クラスの定義を他のクラスの内部に置くために使用されているだけです.しかし、内部クラスの理解が進むにつれて、それだけでなく、外部クラスを定義し、通信し、内部クラスの様々な特徴を利用することができることがわかります.コードをより優雅で明確にすることができます.
基本的な内部クラスの作成方法
上記の内部クラスの定義と同様に、あるクラスの定義を別のクラスの内部に配置すると、内部クラスの作成が完了します.
以下では、チーズピザcheesePizzaとソーセージピザsausagePizzaの2つの内部クラスを定義するピザ店のクラスを作成すると仮定します.
package learning_java.InnerClassTest;

public class PizzaStore {
    class cheesePizza {
        private String name = "cheesePizza";
        public void getName() {System.out.println("Got a " + name);}
    }
    class sausagePizza {
        private String name = "sausagePizza";
        public void getName() {System.out.println("Got a " +name);}
    }
    //               ,               
    public void orderPizza(String pizzaName) {
        if (pizzaName == "cheesePizza") {
            cheesePizza pizza = new cheesePizza();
            pizza.getName();
        }
        else if (pizzaName == "sausagePizza"){
            sausagePizza pizza = new sausagePizza();
            pizza.getName();
        }
        else
            System.out.println("Don't have this kind of pizza");
    }

    public static void main(String[] args) {
        PizzaStore store = new PizzaStore();
        store.orderPizza("cheesePizza");

    }
}

2つのピザの内部クラスを定義した後、ピザを注文する方法orderPizzaを定義しました.この方法では、入力した必要に応じてピザのオブジェクトを作成します.私たちがこの方法で内部クラスを使うときは、普通のクラスと変わらないように見えますが、ここでは、実際の違いは内部クラスの定義時にPizzaStoreクラスにネストされているだけですが、これは唯一の区別ではありません.
public class PizzaStore2 {
    class cheesePizza {
        private String name = "cheesePizza";
        int size;
        cheesePizza(int inputSize){
            size = inputSize;
        }
        public void getName() {System.out.println("Got a " + name + "\tof size:\t" + size);}
    }
    class sausagePizza {
        private String name = "sausagePizza";
        int size;
        sausagePizza(int inputSize){
            size = inputSize;
        }
        public void getName() {System.out.println("Got a " + name + "\tof size:\t" + size);}
    }

    public sausagePizza getSausagePizza(int size){
        return new sausagePizza(size);
    }
    public cheesePizza getCheesePizza(int size){
        return new cheesePizza(size);
    }
    public void orderPizza(String pizzaName) {
        if (pizzaName == "cheesePizza") {
            cheesePizza pizza = new cheesePizza(5);
            pizza.getName();
        }
        else if (pizzaName == "sausagePizza"){
            sausagePizza pizza = new sausagePizza(5);
            pizza.getName();
        }
        else
            System.out.println("Don't have this kind of pizza");
    }
    //                               ,      main()     ,            :OuterClassName.InnerClassName
    public static void main(String[] args) {
        PizzaStore2 store = new PizzaStore2();
        store.orderPizza("cheesePizza");

        PizzaStore2 store2 = new PizzaStore2();
        PizzaStore2.cheesePizza pizza = store2.getCheesePizza(6);
        pizza.getName();
    }
}

上記のコードでは、2つのピザの構造関数を書き、getCheesePizzagetSausagePizzaの方法でこの2つのピザの例を返しますが、main()で宣言するときに使用します.この定義と同様に、内部クラスについて、外部クラスの非静的メソッド以外の任意の場所から内部クラスのオブジェクトを作成するには、main()メソッドのように、このオブジェクトのタイプ:OuterClassName.を具体的に知らなければならない.InnerClassName.すなわち、その外部クラスの内部クラスを定義する静的な方法であっても、その外部クラス以外のすべてのクラスはできない.
例を挙げると、ピザ店pizzaStore 2に以下の方法を加えると、そのインスタンスの作成方法は、前のPizzaStore2.cheesePizzaの方法と全く同じであることがわかります.
public static void test() {
        cheesePizza pizza = new cheesePizza(5);
}

エラーが表示されます.
Error:(29, 29) java:                   this

内部クラスと外部クラスのリンク
現在の位置では、内部はまだコードを組織するモードに似ていますが、すぐに別の用途を見ることができます.内部クラスのオブジェクトを生成すると、そのオブジェクトとその周辺オブジェクト(enclosing object)との間には、特別な条件を必要とせずに周辺オブジェクトのすべてのメンバーにアクセスできるようにするつながりがあります.また、内部クラスは、周辺オブジェクトだけでなく、その周辺クラスのすべての要素にアクセスできます.
public class PizzaStore3 {
    private int materialsNum;
    private static int storeNum = 0;
    private static int allPizzaNum = 0;
    public void showMaterialsNum() {System.out.println("the num of materials:\t" + materialsNum);}
    public static void showStoreNum() {System.out.println("the num of store:\t" + storeNum);}
    public static void showAllPizzaNum() {System.out.println("the num of all the pizza:\t" + allPizzaNum);}
    public PizzaStore3(int materialsNum) {
        this.materialsNum = materialsNum;
        storeNum ++;
    }
    class cheesePizza implements Pizza{
        private String name = "cheesePizza";
        int size;
        cheesePizza(int inputSize){
            size = inputSize;
            materialsNum -= size;
            allPizzaNum ++;
        }
        public void getName() {System.out.println("Got a " + name + "\tof size:\t" + size);}
    }
    class sausagePizza implements Pizza{
        private String name = "sausagePizza";
        int size;
        sausagePizza(int inputSize){
            size = inputSize;
            materialsNum -= size;
            allPizzaNum ++;
        }
        public void getName() {System.out.println("Got a " + name + "\tof size:\t" + size);}
    }

    public sausagePizza getSausagePizza(int size){
        return new sausagePizza(size);
    }
    public cheesePizza getCheesePizza(int size){
        return new cheesePizza(size);
    }

    public void orderPizza(String pizzaName) {
        Pizza pizza;
        switch(pizzaName) {
            case "cheesePizza": pizza = new cheesePizza(5);break;
            case "sausagePizza": pizza = new sausagePizza(5);   break;
            default:    System.out.println("Don't have this kind of pizza"); return;
        }
        pizza.getName();
    }
    public static void main(String[] args) {
        PizzaStore3 store = new PizzaStore3(100);

        store.showMaterialsNum();
        PizzaStore3.showAllPizzaNum();

        PizzaStore3.cheesePizza pizza = store.getCheesePizza(6);
        pizza.getName();

        store.showMaterialsNum();
        PizzaStore3.showAllPizzaNum();
    }
}

ここで、私たちは依然として私たちのピザ屋を例にして、今回、私たちはピザ屋ごとにorderPizza型のメンバーintを追加して店の原料の数を記録して、私たちはピザを作るたびにその大きさと同じ数の原料を消費します.同時に、私たちのmaterialsNumクラスに静的変数PizzaStore3を追加して、私たちのすべてのピザ屋が作ったピザの数を記録しました.ピザの構造関数を呼び出してピザを作るたびに、私たちはこのグローバルな数に1つ追加しました.
その後、allPizzaNumにstoreを新規作成し、storeにpizzaを作成し、以上の情報を印刷しました.
the num of materials:	100
the num of all the pizza:	0
Got a cheesePizza	of size:	6
the num of materials:	94
the num of all the pizza:	1

オブジェクトのメンバーに対しても、クラスの静的メンバーに対しても、内部クラスは部屋にアクセスして変更できることがわかります.内部クラスmain()cheesePizzaをよく見ると、その構築関数は変数sausagePizzaを使用しますが、この変数はこの2つの内部クラスに属しているのではなく、周辺クラスmaterialsに属するpizzaStoreフィールドです.ここでは参照です.これにより、内部クラスは、自分が持っているように、周辺クラスの方法とフィールドにアクセスできます.これは大きな便利さをもたらした.
すべての内部クラスは自動的にその外周クラスのすべてのメンバーにアクセス権を持っていますが、これはどのようにしてできますか?実は、ある周辺クラスのオブジェクトが内部クラスのオブジェクトを作成した場合、この内部クラスのオブジェクトは必ず秘密裏にその周辺クラスのオブジェクトへの参照をキャプチャし、その後、語の周辺クラスのメンバーにアクセスする際に、その参照で周辺クラスのメンバーを選択します.この一連のプロセスでは、コンパイラはすべての詳細を処理するのに役立ちますが、内部クラスのオブジェクトは、その周辺クラスのオブジェクトに関連付けられた場合にのみ作成されます(内部クラスがprivateではない場合)、内部クラスのオブジェクトを構築するには、その周辺クラスのオブジェクトへの参照が必要です.コンパイラがこのリファレンスにアクセスできない場合は、エラーが表示されます.内部クラスとその周辺クラスのこのような関係は,我々の次の節の内容を引き出した.
通過するこれは内部クラスでその外周クラスを参照するために使用されます.
上記の節で述べたように、1つの外部クラスの内部クラスのオブジェクトは、この外部クラスのオブジェクトとリンクの依存関係があるので、内部クラスでこの外部クラスのオブジェクトを明示的に呼び出すことができるのではないでしょうか.答えは肯定的だ.内部クラスで周辺クラスオブジェクトへの参照を生成する場合は、外部クラスの名前の後にstaticを付けることで、生成された参照が自動的に正しいタイプを持つことができ、コンパイル期間中に認識され、チェックされるため、実行時のオーバーヘッドはありません.これを以下の簡略化されたピザ店のクラスで示します.
public class PizzaStore4 {
    void getStoreName() {System.out.println("PizzaStore4!");}
    public class CheesePizza {
        public PizzaStore4 getStore() {
            //      .this       
            return PizzaStore4.this;
        }
    }
    public CheesePizza cheesePizza() {return new CheesePizza();}

    public static void main(String[] args) {
        PizzaStore4 store = new PizzaStore4();
        PizzaStore4.CheesePizza pizza = store.cheesePizza();
        pizza.getStore().getStoreName();
    }
}

出力:
PizzaStore4!

通過するnewは内部クラスのオブジェクトを作成します
第1節では、内部クラスのオブジェクトを作成する際に、.thisgetCheesePizzaのような外部クラスで提供される新しい内部クラスのオブジェクトを返す方法を使用しますが、外部クラスでは必ずしもこのような方法が提供されていません.では、内部クラスのオブジェクトを直接得るにはどうすればいいのでしょうか.
内部クラスと外部クラスのオブジェクトのリンクのセクションで説明したように、各内部クラスは外部クラスの1つのオブジェクトとリンクされているので、内部クラスのオブジェクトを作成する際にも、この点を体現する必要があります.私たちは直接getSausagePizzaの内部クラスのオブジェクトを出すことはできません.では、外部クラスのオブジェクトとどのように関連しますか.ここでは、new構文を使用する必要があります.次の例では、この構文の使用方法について説明します.
public class PizzaSotre5 {
    public class CheesePizza{}
    public static void main(String[] args) {
        PizzaSotre5 store = new PizzaSotre5();
        //   .new         
        PizzaSotre5.CheesePizza pizza = store.new CheesePizza();
    }
}
.newで直接作成された内部クラスオブジェクトPizzaStoreであるため、内部クラスオブジェクトと外部クラスオブジェクトとのリンクと内部クラスオブジェクトの役割ドメインの問題を直接解決することができる.実際、ここでは外部クラスの名前pizzaを引用することはできません.オブジェクトを使用して内部クラスのオブジェクトを創出する必要があります.PizzaStore5も必要ありません.
これにより、外部クラスのオブジェクトを持つ前に、内部クラスのオブジェクトを作成できるお腹が空いていないことがわかります.これは、内部クラスオブジェクトが外部クラスを作成するオブジェクトに自動的に接続されるためです.ただし、ネストされたクラス(静的内部クラス)を作成する場合は、外部クラスオブジェクトへの参照は必要ありません.