JavaRush /Java Blog /Random-JA /入れ子になった内部クラスまたは Java の内部クラス

入れ子になった内部クラスまたは Java の内部クラス

Random-JA グループに公開済み
こんにちは!今日は、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("Go!");
   }

   public class HandleBar {

       public void right() {
           System.out.println("Steering wheel to the right!");
       }

       public void left() {

           System.out.println("Steering wheel to the left!");
       }
   }

   public class Seat {

       public void up() {

           System.out.println("The seat is up!");
       }

       public void down() {

           System.out.println("The seat is down!");
       }
   }
}
ここには自転車というクラスがありますBicycle。2 つのフィールドと 1 つのメソッドがありますstart()。 通常のクラスとの違いは、クラス (ハンドル) とクラス(シート)入れ子になった内部クラス - 3のコードが内部に記述されている 2 つのクラスがあることです。これらは本格的なクラスです。ご覧のとおり、それぞれに独自のメソッドがあります。この時点で、「なぜあるクラスを別のクラスの中に入れたのか」という疑問が生じるかもしれません。なぜ内部化するのでしょうか? さて、プログラム内でハンドルとシートに別のクラスが必要だとしましょう。ただし、ネストする必要はありません。定期的に授業を行うことができます。たとえば、次のようになります。 BicycleHandleBarSeat
public class HandleBar {
   public void right() {
       System.out.println("Steering wheel to the right!");
   }

   public void left() {

       System.out.println("Steering wheel left");
   }
}

public class Seat {

   public void up() {

       System.out.println("The seat is up!");
   }

   public void down() {

       System.out.println("The seat is 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();
   }
}
コンソール出力:

Сиденье поднято выше!
Поехали!
Руль влево!
Руль вправо!
何が起こっていたのかが突然理解できました!:) 自転車オブジェクトを作成しました。ステアリング ホイールとシートという 2 つの「サブオブジェクト」を作成しました。便宜上、シートを高く上げて出発しました。進むべき方向に回転して操縦します。:) 必要なメソッドは必要なオブジェクトに対して呼び出されます。すべてがシンプルで便利です。この例では、ハンドルバーとシートを強調表示することでカプセル化が強化され (対応するクラス内の自転車の部品に関するデータを非表示にしています)、より詳細な抽象化を作成できるようになります。次に、別の状況を見てみましょう。自転車と部品店をモデル化するプログラムを作成したいとします。 入れ子になった内部クラス - 4この状況では、以前のソリューションは失敗します。部品屋という枠の中で、自転車の本質とは別に、自転車の部品ひとつひとつにも意味があります。たとえば、「ペダルを購入者に売る」「新しいシートを購入する」などのメソッドが必要になります。ここで内部クラスを使用するのは間違いです。新しいプログラム内の自転車の個々の部分にはそれぞれ独自の意味があります。つまり、自転車の本質とは別のものであり、自転車の本質とは決して結びついておりません。内部クラスを使用する必要があるか、すべてのエンティティを個別のクラスに分離する必要があるかどうかを疑問に思っている場合は、これに注意する必要があります。オブジェクト指向プログラミングは、現実世界のエンティティを簡単にモデル化できるため、優れています。これは、内部クラスを使用するかどうかを決定する際のガイドとして使用できます。実店舗では、部品が自転車とは別になっていますが、これは正常です。これは、プログラムを設計するときにこれが正しいことを意味します。さて、「哲学」を整理しました:) 次に、内部クラスの重要な「技術的」機能について見てみましょう。絶対に覚えて理解する必要があることは次のとおりです。
  1. 内部クラスのオブジェクトは、「外部」クラスのオブジェクトなしでは存在できません。

    これは論理的です。だからこそ、所有者のいないハンドルやシートがプログラムのあちこちに登場しないように、Seat内部クラスにしました。HandleBar

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

    public static void main(String[] args) {
    
       HandleBar handleBar = new HandleBar();
    }

    ここから、次の重要な特徴が得られます。

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

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

    次に、内部クラスで、seat パラメータを通知する Seatメソッドを作成できます。getSeatParam()

    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("Go!");
       }
    
       public class Seat {
    
           public void up() {
    
               System.out.println("The seat is up!");
           }
    
           public void down() {
    
               System.out.println("The seat is down!");
           }
    
           public void getSeatParam() {
    
               System.out.println("Seat parameter: 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.getSeatParam();
       }
    }

    コンソール出力:

    
    Параметр сиденья: диаметр подседельного штыря = 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 {
    
           //inner class cannot have static declarations
           public static void getSeatParam() {
    
               System.out.println("Seat parameter: seatpost diameter = " + Bicycle.this.seatPostDiameter);
           }
       }
    }
  5. 内部クラス オブジェクトを作成する場合、そのアクセス修飾子が重要な役割を果たします。

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

    どうしてそれが重要ですか?

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

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

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

    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 a 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