未来エンジニア養成所Blog

月単価180万以上のプログラミング講師がプログラミングを皆に楽しんでもらうための情報をお届けします。

【Java】内部クラス Part2

title

前回に引き続き、内部クラスについて解説します。

前回の記事はこちら phoeducation.work


メンバーインナークラス

メンバーインナークラスはクラスのメンバとして定義されたクラスです。

クラスにはこれまで見てきたようにフィールドやメソッドがありますが、それらと同じようにクラスの一つ内側(mainメソッドや他のブロックの外側)に定義します。

アクセス方法はインスタンスを作成する場所によって異なります。

外側のクラス内でインスタンス化する場合と他のクラスでインナークラスのインスタンスを作成する場合を見ていきましょう。


【メンバーインナークラス1】

class Outer3{
    Outer3(){
        System .out.println("Outer3クラスがインスタンス化されました");
        Inner3 in = new Inner3();
        in.var();
    }

    class Inner3{
        Inner3(){
            System .out.println("Inner3クラスがインスタンス化されました");
        }
        void var(){
            System .out.println("Inner3クラスのvar()が呼ばれました");
        }

    }
    void foo(){
        System .out.println("Outer3クラスのfoo()が呼ばれました");
    }
}

public class NestSample3 {
    public static void main(String[] args){
        Outer3 out = new Outer3();
        out.foo();
    }

}


実行結果

Outer3クラスがインスタンス化されました
Inner3クラスがインスタンス化されました
Inner3クラスのvar()が呼ばれました
Outer3クラスのfoo()が呼ばれました


title


1行目~20行目までのOuter3クラスには、メンバとしてコンストラクタとInner3メンバーインナークラス、foo()の3つが定義されています。

22行目のNestSample3クラスのmain()メソッドの24行目では、Outer3クラスを利用するためにインスタンス化をしています。

Outer3クラスがインスタンス化されると、2行目~6行目のコンストラクタが呼ばれます。

次に4行目でInner3クラスを利用するため、Inner3クラスをインスタンス化しています。

これにより9行目~11行目のInner3クラスのコンストラクタが呼ばれます。

次に5行目でInner3クラスのインスタンスinでInner3クラスのvar()を呼び出しています。

ここまでで24行目のOuter3のインスタンス化による動作が終了し、main()の25行目に制御が戻ってきます。

25行目では、これまで通り、Outer3クラスのインスタンスoutでfoo()を呼び出しています。

このようにメンバーインナークラスを利用するには、staticでないフィールドやメソッドと同様に、メンバーインナークラスを持つ外側のクラス(Outer3)をインスタンス化する必要があります。


【メンバーインナークラス2】

class Outer4{
    Outer4(){
        System .out.println("Outer4クラスがインスタンス化されました");
    }

    class Inner4{
        Inner4(){
            System .out.println("Inner4クラスがインスタンス化されました");
        }
    }
}

public class NestSample4 {
    public static void main(String[]args){
        Outer4 out = new Outer4();
        Outer4.Inner4 in0 = out.new Inner4();

        //Outer4.Inner4 in1 = new Outer4().new Inner4();
    }
}

実行結果

Outer4クラスがインスタンス化されました
Inner4クラスがインスタンス化されました


Inner4クラスを利用するために、まずはInner4クラスの外側のOuter4クラスを15行目でインスタンス化しています。

これにより、2行目~4行目のOuter4クラスのコンストラクタが実行されます。

次にmain()の16行目でInner4クラスをインスタンス化します。

左辺の型はOuter4に所属するInnner4なのでOuter4.Innner4と記述をします。

右辺のインスタンス化は、15行目で取得したOuter4クラスのインスタンスoutを使ってInner4クラスをインスタンス化します。

18行目の記述は、15行目と16行目の2行の記述を、1行にまとめて記述したものです。


ローカルインナークラス

メソッドの中でもクラスを宣言することができます。

メソッド内で定義されたクラスを、「ローカルインナークラス」と呼びます。

ローカルインナークラスは、そのメソッド内のみで利用できるクラスを定義するときに使用します。

ローカルインナークラスの定義は、ローカル変数と同様に、メソッドブロック内に記述をします。

ローカルインナークラスを使用するには、そのインナークラスをインスタンス化する必要があり、そのインスタンス化のコードは、メソッド内のクラス定義の後に記述しなればなりません。

言い換えると、メソッドのスコープ外で、インスタンス化するコードを記述することはできません。


【ローカルインナークラス】

public class LocalSample{
    public static void main(String[] args){
        foo();
    }
    public static void foo(){
        class LocalInner{
            LocalInner(){
                System.out.println("ローカルインナークラスがインスタンス化されました");
            }
        }
        new LocalInner();
    }
}


実行結果

ローカルインナークラスがインスタンス化されました


ローカルインナークラスはメソッド内のみで有効なクラスです。

そのため、ローカルインナークラスを使用する場合もそのメソッドのスコープ内でインスタンス化をする必要があります。

main()の3行目で5行目のfoo()が呼ばれています。

foo()の6行目~10行目はLocalInnerクラスを定義しているだけで、何らかの動作ではありません。

foo()の動作としては、11行目のLocalInnerクラスのインスタンス化から始まります。

LocalInnerクラスがインスタンス化されると、7行目~9行目のLocalInnerクラスのコンストラクタが呼ばれます。


無名インナークラス

無名インナークラスは匿名クラスともよばれる、ローカルインナークラスの一つです。

無名インナークラスは、その場限りのサブクラスを定義し、オーバーライドしたメソッドを利用するもので、再利用しないため、名前を付けない事から、無名インナークラスと呼ばれます。

無名インナークラスでは、その定義とインスタンス化をまとめて行います。

このクラスの定義を終えるには、中括弧で閉じ、セミコロンを付けます “ }; ”

無名インナークラスを使うと、抽象クラスやインターフェースの実装クラスの定義とインスタンス化を同時に行うことができます。


【無名インナークラスを使用しない例】

class MySuper1{
    void foo(){
        System.out.println("スーパクラスのfoo()が呼ばれました");
    }
}
class MySub1 extends MySuper1{
    void foo(){
        System.out.println("サブクラスのfoo()が呼ばれました");
    }
}
public class AnonymousSample1 {
    public static void main(String[] args) {
        MySuper1 sp  = new MySub1();
        sp.foo();
    }
}

実行結果

サブクラスのfoo()が呼ばれました


MySuper1というスーパークラスにあるfoo()をMySub1というサブクラスが継承し、foo()をオーバライドしています。

main()の13行目でMySuper1クラスの型でMySub1クラスをインスタンス化し、14行目でfoo()を呼び出すと、オーバーライドされたMySub1クラスのfoo()が呼ばれます。

これは継承関係にあるクラスでメソッドがオーバーライドされたときの仕組みの確認です。


【無名インナークラスを使用した例】

class MySuper2{
    void foo(){
        System.out.println("スーパクラスのfoo()が呼ばれました");
    }
}

public class AnonymousSample2 {
    public static void main(String[] args) {
        MySuper2 sp  = new MySuper2(){
            void foo(){
                System.out.println("無名インナークラスのfoo()が呼ばれました");
            }
        };
        sp.foo();
    }
}

実行結果

無名インナークラスのfoo()が呼ばれました


MySuper2というスーパークラスにあるfoo()をオーバーライドするには、先程の例のように、通常はサブクラスを作成し、foo()を実装しますが、そのサブラスは今回限りで、ここでしか使わない場合は、わざわざ名前を付けてサブクラスを作る必要はありません。

名前をつけないサブクラスの定義は9行目の{~13行目の}までです。

その中でfoo()をオーバーライドしています。

13行目の末尾のセミコロン(;)は9行目のMySuper2 sp = new MySuper2()の命令の最後に記述するセミコロンです。

すなわち9行目~13行目でMySuper2クラスのサブクラスを名前を付けないで定義し、同時にインスタンス化までしているわけです。

無名クラスのインスタンスは変数spに格納されていますから、14行目でそのインスタンスのfoo()を呼び出すと、無名インナークラスでオーバーライドされたfoo()が呼ばれることになります。

まとめ

  • 内部クラス

    • 内部クラスとはクラスの中にクラスを定義したものです。
    • 内部クラスにはネストクラス、メンバーインナークラス、ローカルインナークラスなどの種類があります。
    • ローカルインナークラスは、継承を用いるときには無名クラスにしても良いです。



独学で挫折しそうになったら、オンラインプログラミングスクール
未来エンジニア養成所Logo



あわせて学習したい

phoeducation.work phoeducation.work