1. 概要

Javaクラスをコンパイルすると、同じ名前のクラスファイルが作成されます。 ただし、ネストされたクラスまたはネストされたインターフェイスの場合は、ドル記号を含む、内部クラス名と外部クラス名を組み合わせた名前のクラスファイルが作成されます。

この記事では、これらすべてのシナリオを見ていきます。

2. 詳細

Javaでは、クラス内にクラスを作成できます。 内部に記述されたクラスはネストされたクラスと呼ばれ、ネストされたクラスを保持するクラスは外部クラスと呼ばれます。 ネストされたクラスのスコープは、それを囲むクラスのスコープによって制限されます。

同様に、別のインターフェイスまたはクラス内でインターフェイスを宣言できます。 このようなインターフェースは、ネストされたインターフェースと呼ばれます。

ネストされたクラスとインターフェイスを使用して、1か所でのみ使用されるエンティティを論理的にグループ化できます。 これにより、コードが読みやすく保守しやすくなるだけでなく、カプセル化も向上します。

次のセクションでは、これらのそれぞれについて詳しく説明します。 列挙型も見ていきます。

3. ネストされたクラス

ネストされたクラスは、別のクラスまたはインターフェイス内で宣言されたクラスです。 別のクラスが必要であるが、そのクラスを別のクラスの一部として動作させたい場合は、ネストされたクラスがそれを実現するための最良の方法です。

Javaファイルをコンパイルすると、それを囲むクラス用に .class ファイルが作成され、ネストされたすべてのクラス用に個別のクラスファイルが作成されます。 囲んでいるクラス用に生成されたクラスファイルは、Javaクラスと同じ名前になります。

ネストされたクラスの場合、 コンパイラは異なる命名規則を使用します– OuterClassName $ NestedClassName.class

まず、簡単なJavaクラスを作成しましょう。

public class Outer {

// variables and methods...
}
Outside classをコンパイルすると、コンパイラはOutside.classファイルを作成します。
次のサブセクションでは、外部クラスにネストされたクラスを追加し、クラスファイルの名前を確認します。

3.1. 静的にネストされたクラス

名前が示すように、 static として宣言されているネストされたクラスは、静的なネストされたクラスと呼ばれます。 Javaでは、ネストされたクラスのみがstaticになります。

静的にネストされたクラスは、静的および非静的の両方のフィールドとメソッドを持つことができます。 それらは、特定のインスタンスではなく、外部クラスに関連付けられています。 したがって、それらにアクセスするために外部クラスのインスタンスは必要ありません。

Outsideクラス内で静的にネストされたクラスを宣言しましょう。

public class Outer {
    static class StaticNested {
        public String message() {
            return "This is a static Nested Class";
        }
    }
}

Outside class をコンパイルすると、コンパイラは2つのクラスファイルを作成します。1つは Outside 用で、もう1つはStaticNested用です。

3.2. 非静的ネストクラス

非静的ネストクラス(内部クラスとも呼ばれます)は、囲んでいるクラスのインスタンスに関連付けられており、外部クラスのすべての変数とメソッドにアクセスできます。

外部クラスはパブリックアクセスまたはデフォルトアクセスのみを持つことができますが、内部クラスはプライベート、パブリック、保護、またはデフォルトアクセスを持つことができます。 ただし、静的メンバーを含めることはできません。 また、内部クラスにアクセスするには、外部クラスのインスタンスを作成する必要があります。

外部クラスにネストされたクラスをもう1つ追加しましょう。

public class Outer {
    class Nested {
        public String message() {
            return "This is a non-static Nested Class";
        }
    }
}

もう1つのクラスファイルを生成します。

3.3. ローカルクラス

内部クラスとも呼ばれるローカルクラスは、バランスの取れた中括弧の間のステートメントのグループであるブロックで定義されます。 たとえば、メソッド本体、 for ループ、またはif句に含めることができます。 ローカルクラスのスコープは、ローカル変数と同様にブロック内に制限されます。 ローカルクラスは、コンパイルされると、自動生成された数値が付いたドル記号として表示されます。

ローカルクラス用に生成されたクラスファイルは、命名規則を使用します– OuterClassName $ 1LocalClassName.class

メソッド内でローカルクラスを宣言しましょう。

public String message() {
    class Local {
        private String message() {
            return "This is a Local Class within a method";
        }
    }
    Local local = new Local();
    return local.message();
}

コンパイラは、Localクラス用に別のクラスファイルを作成します。

同様に、if句内でローカルクラスを宣言できます。

public String message(String name) {
    if (StringUtils.isEmpty(name)) {
        class Local {
            private String message() {
                return "This is a Local class within if clause";
            }
        }
        Local local = new Local();
        return local.message();
    } else
        return "Welcome to " + name;
}

同じ名前の別のローカルクラスを作成していますが、コンパイラは文句を言いません。 もう1つのクラスファイルを作成し、番号を増やして名前を付けます。

3.4. 匿名の内部クラス

名前が示すように、匿名クラスは名前のない内部クラスです。 コンパイラは、ドル記号の後に自動生成された番号を使用して、クラスファイルに名前を付けます。

匿名クラスを単一の式で同時に宣言してインスタンス化する必要があります。 これらは通常、既存のクラスを拡張するか、インターフェースを実装します。

簡単な例を見てみましょう:

public String greet() {
    Outer anonymous = new Outer() {
        @Override
        public String greet() {
            return "Running Anonymous Class...";
        }
    };
    return anonymous.greet();
}

ここでは、 Outside クラスを拡張して匿名クラスを作成し、コンパイラーがもう1つのクラスファイルを追加しました。

同様に、匿名クラスを使用してインターフェースを実装できます。

ここでは、インターフェースを作成しています。

interface HelloWorld {
    public String greet(String name);
}

それでは、匿名クラスを作成しましょう。

public String greet(String name) {
    HelloWorld helloWorld = new HelloWorld() {
        @Override
        public String greet(String name) {
            return "Welcome to "+name;
        }
    };
    return helloWorld.greet(name);
}

クラスファイルの改訂されたリストを観察してみましょう。

ご覧のとおり、インターフェイス HelloWorld 用のクラスファイルと、 Outside $2という名前の匿名クラス用のクラスファイルが生成されます。

3.5. インターフェイス内の内部クラス

別のクラス内のクラスを見てきました。さらに、インターフェイス内でクラスを宣言できます。 クラスの機能がインターフェースの機能と密接に関連している場合は、インターフェース内で宣言できます。 インターフェイスメソッドのデフォルトの実装を記述したい場合は、この内部クラスを選択できます。

HelloWorldインターフェース内で内部クラスを宣言しましょう。

interface HelloWorld {
    public String greet(String name);
    class InnerClass implements HelloWorld {
        @Override
        public String message(String name) {
            return "Inner class within an interface";
        }
    }
}

そして、コンパイラはもう1つのクラスファイルを生成します。

4. ネストされたインターフェイス

ネストされたインターフェイスは、内部インターフェイスとも呼ばれ、クラスまたは別のインターフェイス内で宣言されます。 ネストされたインターフェイスを使用する主な目的は、関連するインターフェイスをグループ化することによって名前空間を解決することです。

ネストされたインターフェイスに直接アクセスすることはできません。 これらには、外部クラスまたは外部インターフェイスを使用してのみアクセスできます。 たとえば、Mapインターフェイス内のEntryインターフェイスはネストされており、MapEntryとしてアクセスできます。

ネストされたインターフェイスを作成する方法を見てみましょう。

4.1. インターフェイス内部のインターフェイス

インターフェイス内で宣言されたインターフェイスは、暗黙的にパブリックです。

HelloWorldインターフェース内でインターフェースを宣言しましょう。

interface HelloWorld {
    public String greet(String name);
    
    interface HelloSomeone{
        public String greet(String name);
    }
}

これにより、ネストされたインターフェイス用に HelloWorld $HelloSomeoneという名前の新しいクラスファイルが作成されます。

4.2. クラス内のインターフェース

クラス内で宣言されたインターフェースは、任意のアクセス修飾子を取ることができます。

Outsideクラス内でインターフェースを宣言しましょう。

public class Outer {
     interface HelloOuter {
        public String hello(String name);
    }
}

OutsideClass $StaticNestedClassという名前の新しいクラスファイルが生成されます。

5. 列挙

列挙型はJava5で導入されました。 これは定数の固定セットを含むデータ型であり、それらの定数はその列挙型のインスタンスです。

enum 宣言は、e num タイプ(列挙データ型とも呼ばれます)と呼ばれるクラスを定義します。 enum には、コンストラクター、メソッド、変数、定数固有のクラス本体と呼ばれるものなど、さまざまなものを追加できます。

enum を作成すると、新しいクラスが作成され、Enumクラスが暗黙的に拡張されます。 Enum は、他のクラスを継承できないか、拡張できません。 ただし、インターフェイスを実装できます。

enum は、スタンドアロンクラス、独自のソースファイル、または別のクラスメンバーとして宣言できます。 列挙型を作成するすべての方法を見てみましょう。

5.1. クラスとしての列挙

まず、単純な列挙型を作成しましょう。

enum Level {
    LOW, MEDIUM, HIGH;
}

コンパイルされると、コンパイラは列挙型のLevelという名前のクラスファイルを作成します。

5.2. クラス内の列挙型

次に、外部クラスでネストされた列挙型を宣言しましょう。

public class Outer {
    enum Color{ 
        RED, GREEN, BLUE; 
    }
}

コンパイラは、ネストされた列挙型用に Outside $Colorという名前の別のクラスファイルを作成します。

5.3. インターフェイス内の列挙型

同様に、インターフェイス内でenumを宣言できます。

interface HelloWorld {
    enum DIRECTIONS {
        NORTH, SOUTH, EAST, WEST;
    }
}

HelloWorld インターフェイスがコンパイルされると、コンパイラは HelloWorld$Directonという名前のクラスファイルをもう1つ追加します。

5.4. 列挙内の列挙

別の列挙型内で列挙型を宣言できます。

enum Foods {
    DRINKS, EATS;
    enum DRINKS {
        APPLE_JUICE, COLA;
    }
    enum EATS {
        POTATO, RICE;
    }
}

最後に、生成されたクラスファイルを見てみましょう。

コンパイラは、列挙型タイプごとに個別のクラスファイルを作成します。

6. 結論

この記事では、Javaクラスファイルに使用されるさまざまな命名規則について説明しました。 単一のJavaファイル内にクラス、インターフェース、および列挙型を追加し、コンパイラーがそれらごとに個別のクラスファイルを作成する方法を観察しました。

いつものように、この記事のコード例はGitHubから入手できます。