CodeGym /Java Blog /ランダム /入れ子になった内部クラス
John Squirrels
レベル 41
San Francisco

入れ子になった内部クラス

ランダム グループに公開済み
やあ!今日は、Java で入れ子になったクラスがどのように機能するかという重要なトピックを取り上げます。Java では、別のクラス内にクラスを作成できます。

class OuterClass {
    ...
    static class StaticNestedClass {
        ...
    }
    class InnerClass {
        ...
    }
}
これらの内部クラスはネストされたクラスと呼ばれます。それらは 2 つのタイプに分類されます。
  1. 非静的ネストされたクラス。これらは内部クラスとも呼ばれます。
  2. 静的な入れ子になったクラス。
さらに、内部クラスには 2 つの異なるサブカテゴリーがあります。内部クラスは、単に内部クラスであるだけでなく、次のこともできます。
  • ローカルクラス
  • 匿名クラス
混乱している?:) 大丈夫。わかりやすくするために図を示します。突然混乱した場合は、レッスン中に戻ってください。 入れ子になった内部クラス - 2今日のレッスンでは、内部クラス (非静的入れ子クラスとも呼ばれます) について説明します。これらは、全体図の中で特に強調表示されているので、迷わないように :) 明白な質問から始めましょう: なぜそれらは「内部」クラスと呼ばれるのでしょうか? 答えは非常に簡単です。それらは他のクラス内で作成されるからです。以下に例を示します。

public class Bicycle {

   private String model;
   private int weight;

   public Bicycle(String model, int weight) {
       this.model = model;
       this.weight = weight;
   }
  
   public void start() {
       System.out.println("Let's go!");
   }

   public class Handlebar {

       public void right() {
           System.out.println("Steer right!");
       }

       public void left() {

           System.out.println("Steer left!");
       }
   }

   public class Seat {
      
       public void up() {

           System.out.println("Seat up!");
       }
      
       public void down() {

           System.out.println("Seat down!");
       }
   }
}
ここにクラスがありますBicycle。これには 2 つのフィールドと 1 つのメソッドがあります: start()。 これは、と の入れ子になった内部クラス - 32 つのクラスが含まれるという点で通常のクラスとは異なります。コードはクラス内に記述されます。これらは本格的なクラスです。ご覧のとおり、それぞれに独自のメソッドがあります。この時点で、「一体なぜ、あるクラスを別のクラスの中に入れる必要があるのでしょうか?」という疑問が生じるかもしれません。なぜ内部クラスを作るのでしょうか? さて、プログラム内でハンドルバーとシートの概念に別のクラスが必要だとします。もちろん、それらを入れ子にする必要はありません。普通のクラスも作れます。たとえば、次のようになります。 HandlebarSeatBicycle

public class Handlebar {
   public void right() {
       System.out.println("Steer right!");
   }

   public void left() {

       System.out.println("Steer left");
   }
}

public class Seat {

   public void up() {

       System.out.println("Seat up!");
   }

   public void down() {

       System.out.println("Seat down!");
   }
}
とても良い質問です!もちろん、テクノロジーに制限されるわけではありません。そうすることも確かに選択肢です。ここで重要なことは、特定のプログラムとその目的の観点からクラスを正しく設計することです。内部クラスは、別のエンティティと密接に関係しているエンティティを分離するためのものです。ハンドルバー、シート、ペダルは自転車の構成要素です。自転車から切り離してはあまり意味がありません。これらすべての概念を個別のパブリック クラスにすると、プログラムには次のようなコードが含まれることになります。

public class Main {

   public static void main(String[] args) {
       Handlebar handlebar = new Handlebar();
       handlebar.right();
   }
}
うーん...このコードの意味を説明するのはさらに難しいです。曖昧なハンドルバーがあります (なぜそれが必要なのでしょうか? 正直に言うとわかりません)。そして、このハンドルは右に曲がります…自転車なしで、単独で…何らかの理由で。ハンドルバーの概念を自転車の概念から分離することにより、プログラム内のロジックの一部が失われてしまいました。内部クラスを使用すると、コードは大きく異なります。

public class Main {

   public static void main(String[] args) {

       Bicycle peugeot = new Bicycle("Peugeot", 120);
       Bicycle.Handlebar handlebar = peugeot.new Handlebar();
       Bicycle.Seat seat = peugeot.new Seat();

       seat.up();
       peugeot.start();
       handlebar.left();
       handlebar.right();
   }
}
コンソール出力:

Seat up! 
Let's go! 
Steer left! 
Steer right!
今、私たちが見ているものが突然意味をなすようになりました。:) 自転車のオブジェクトを作成しました。ハンドルバーとシートという 2 つの自転車の「サブオブジェクト」を作成しました。快適さを求めてシートを上げて出発しました。必要に応じてペダリングとステアリングを踏みます。:) 必要なメソッドは、適切なオブジェクトで呼び出されます。どれもシンプルで便利です。この例では、ハンドルバーとシートを分離することでカプセル化が強化され (自転車の部品に関するデータを関連クラス内に隠します)、より詳細な抽象化を作成できるようになります。次に、別の状況を見てみましょう。自転車店と自転車のスペアパーツをシミュレートするプログラムを作成するとします。 入れ子になった内部クラス - 4この状況では、以前の解決策は機能しません。自転車店では、自転車から切り離されても、個々の自転車部品に意味があります。たとえば、「顧客にペダルを販売する」、「新しいシートを購入する」などのメソッドが必要になります。ここで内部クラスを使用するのは間違いです。新しいプログラムの個々の自転車パーツには、それぞれに基づいた意味があります。自転車そのものの概念から切り離すことができます。内部クラスを使用するべきか、すべてのエンティティを別のクラスとして編成するべきか迷っている場合は、まさにこれに注意する必要があります。オブジェクト指向プログラミングは、現実世界のエンティティを簡単にモデル化できるという点で優れています。これは、内部クラスを使用するかどうかを決定する際の指針となります。実店舗では、スペアパーツは自転車とは別にありますが、これは問題ありません。つまり、プログラムを設計する際にも大丈夫ということになります。さて、「哲学」を理解しました:) 次に、内部クラスの重要な「技術的」機能について見てみましょう。絶対に覚えて理解する必要があることは次のとおりです。
  1. 内部クラスのオブジェクトは、外部クラスのオブジェクトなしでは存在できません。

    Seatこれは理にかなっています。これが、プログラムにと内部クラスを作成した理由ですHandlebar。これは、孤立したハンドルバーやシートが残らないようにするためです。

    このコードはコンパイルできません:

    
    public static void main(String[] args) {
    
       Handlebar handlebar = new Handlebar();
    }
    

    もう 1 つの重要な特徴は次のとおりです。

  2. 内部クラスのオブジェクトは、外部クラスの変数にアクセスできます。

    たとえば、int seatPostDiameter変数 (シートポストの直径を表す) をBicycleクラスに追加してみましょう。

    次に、内部クラスで、座席のプロパティを表示するメソッド Seatを作成できます。displaySeatProperties()

    
    public class Bicycle {
    
       private String model;
       private int weight;
    
       private int seatPostDiameter;
    
       public Bicycle(String model, int weight, int seatPostDiameter) {
           this.model = model;
           this.weight = weight;
           this.seatPostDiameter = seatPostDiameter;
    
       }
    
       public void start() {
           System.out.println("Let's go!");
       }
    
       public class Seat {
    
           public void up() {
    
               System.out.println("Seat up!");
           }
    
           public void down() {
    
               System.out.println("Seat down!");
           }
    
           public void displaySeatProperties() {
    
               System.out.println("Seat properties: seatpost diameter = " + Bicycle.this.seatPostDiameter);
           }
       }
    }
    

    そして、この情報をプログラムで表示できるようになりました。

    
    public class Main {
    
       public static void main(String[] args) {
    
           Bicycle bicycle = new Bicycle("Peugeot", 120, 40);
           Bicycle.Seat seat = bicycle.new Seat();
    
           seat.displaySeatProperties();
       }
    }
    

    コンソール出力:

    
    Seat properties: seatpost diameter = 40
    

    ノート:新しい変数は、最も厳密なアクセス修飾子 ( private) を使用して宣言されます。それでも内部クラスはアクセスできます。

  3. 内部クラスのオブジェクトを外部クラスの静的メソッド内で作成することはできません。

    これは、内部クラスの編成方法の特定の特徴によって説明されます。内部クラスには、パラメーターを含むコンストラクター、またはデフォルトのコンストラクターのみを含めることができます。しかし、それにもかかわらず、内部クラスのオブジェクトを作成すると、外部クラスのオブジェクトへの参照が、作成された内部クラスのオブジェクトに目に見えない形で渡されます。結局のところ、そのようなオブジェクト参照の存在は絶対的な要件です。そうしないと、内部クラスのオブジェクトを作成できなくなります。

    しかし、外部クラスのメソッドが静的である場合、外部クラスのオブジェクトが存在しない可能性があります。そして、これは内部クラスがどのように機能するかのロジックに違反することになります。この状況では、コンパイラはエラーを生成します。

    
    public static Seat createSeat() {
      
       // Bicycle.this cannot be referenced from a static context
       return new Seat();
    }
    
  4. 内部クラスには静的変数とメソッドを含めることはできません。

    ロジックは同じです。静的メソッドと変数は、オブジェクトが存在しない場合でも存在し、呼び出したり参照したりできます。

    ただし、外部クラスのオブジェクトがなければ、内部クラスにアクセスできません。

    明らかな矛盾です!これが、静的変数と静的メソッドが内部クラスで許可されない理由です。

    これらを作成しようとすると、コンパイラはエラーを生成します。

    
    public class Bicycle {
    
       private int weight;
    
    
       public class Seat {
          
           // An inner class cannot have static declarations
           public static void displaySeatProperties() {
    
               System.out.println("Seat properties: seatpost diameter = " + Bicycle.this.seatPostDiameter);
           }
       }
    }
    
  5. 内部クラスのオブジェクトを作成する場合、そのアクセス修飾子が重要です。

    内部クラスは、標準のアクセス修飾子、publicprivateprotected、およびでマークできますpackage private

    なぜこれが重要なのでしょうか?

    これは、プログラム内の内部クラスのインスタンスを作成できる場所に影響します。

    Seatクラスが として宣言されている場合、他のクラスでオブジェクトをpublic作成できます。Seat唯一の要件は、外部クラスのオブジェクトも存在する必要があることです。

    ちなみに、ここではすでにこれを実行しました。

    
    public class Main {
    
       public static void main(String[] args) {
    
           Bicycle peugeot = new Bicycle("Peugeot", 120);
           Bicycle.Handlebar handlebar = peugeot.new Handlebar();
           Bicycle.Seat seat = peugeot.new Seat();
    
           seat.up();
           peugeot.start();
           handlebar.left();
           handlebar.right();
       }
    }
    

    Handlebarクラスから内部クラスに簡単にアクセスできるようになりましたMain

    内部クラスを として宣言するとprivate、外部クラス内でのみオブジェクトを作成できるようになります。

    Seat「外側」にオブジェクトを作成することはできなくなりました。

    
    private class Seat {
    
       // Methods
    }
    
    public class Main {
    
       public static void main(String[] args) {
    
           Bicycle bicycle = new Bicycle("Peugeot", 120, 40);
    
           // Bicycle.Seat has private access in Bicycle
           Bicycle.Seat seat = bicycle.new Seat();
       }
    }
    

    あなたはおそらくすでにロジックを理解しているでしょう:)

  6. 内部クラスのアクセス修飾子は、通常の変数の場合と同じように機能します。

    修飾子protectedは、同じパッケージ内のサブクラスおよびクラスのインスタンス変数へのアクセスを提供します。

    protected内部クラスでも機能します。protected内部クラスのオブジェクトを作成できます。

    • 外部クラス内。
    • そのサブクラス内。
    • 同じパッケージ内のクラス内。

    内部クラスにアクセス修飾子 ( ) がない場合package private、内部クラスのオブジェクトを作成できます。

    • 外部クラス内。
    • 同じパッケージ内のクラス内。

    修飾子には長い間慣れ親しんでいるので、ここでは問題ありません。

今のところはここまでです:) しかし、手を緩めないでください。内部クラスはかなり広範囲にわたるトピックであり、次のレッスンで引き続き検討していきます。これで、内部クラスに関するコースのレッスンの記憶をリフレッシュできます。次回は、静的ネストされたクラスについて話しましょう。
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION