白猫のメモ帳

C#とかJavaとかJavaScriptとかHTMLとか機械学習とか。

staticでないネストクラス

こんばんは。

昔は海の日から夏休みが始まった気がするのですが、
最近はハッピーマンデーの関係でそうでもないんですよね。

さて、前回のつづきです。
前回はstaticなネストクラスについて書きましたが、
今回は「でない」方です。

Java

Javaにおけるstaticでないネストクラスは、
外側のクラスのインスタンスなしにはアクセスできないクラスです。

class Outer {
    
    private static int field1;
    
    private int field2;
    
    public class Inner {
        
        void notStaticMethod() {
            System.out.println(field1);
            System.out.println(field2);
            System.out.println(Outer.this.field2);
        }
        
        // error!
        // static void staticMethod() {
        // }
    }
}

このネストクラスにはstaticアクセスできないため、当然staticメソッドを定義することはできません。

外側のクラスのインスタンスを「エンクロージングインスタンス」といい、
ネストクラスはstaticでないフィールドにもアクセスすることができます。
(「エンクロージング」は「囲い込む」みたいな意味)

this修飾する場合には「Outer.this.field2」というちょっと不思議な文法を使います。
「this.Outer.field2」ではないことに注意です。

class Test {
    void test() {
        // Outer.Inner inner = new Outer.Inner(); error!
        
        Outer outer = new Outer();
        Outer.Inner inner = outer.new Inner();
        
        Outer.Inner inner2 = new Outer().new Inner();
    }
}

newキーワードによってインスタンスを生成する場合には、エンクロージングインスタンスが必要です。
「outer.new Inner()」という書き方は慣れないと少し変な感じがしますね。

C#

C#のstaticでないネストクラスは前回の記事で触れたとおり、
Javaでのstaticなネストクラスと全く同じ挙動になります。

class Outer
{
    private static int field1;

    private int field2;

    public class Inner
    {
        public void notStaticMethod()
        {
            Console.WriteLine(field1);
            // Console.WriteLine(field2); error!
        }

        public static void staticMethod()
        {
        }
    }
}

class Test
{
    void test()
    {
        Outer.Inner inner = new Outer.Inner();
        Outer.Inner.staticMethod();
    }
}

C#にはエンクロージングインスタンスという概念はありません。

じゃあ外側のインスタンスのメンバには絶対にアクセスできないかというと、
当然そんなことはなくて、単純に外側のクラスのインスタンスを内部に保持してしまえば良いのです。

class Outer
{
    private int field;

    public class Inner
    {
        private readonly Outer outer;
            
        public Inner(Outer outer)
        {
            this.outer = outer;
        }

        public void notStaticMethod()
        {
            Console.WriteLine(this.outer.field);
        }
    }
}

class Test
{
    void test()
    {
        Outer.Inner inner = new Outer.Inner(new Outer());
    }
}

こんな感じ。
自分で作るの意外と面倒・・・。

Javaのインナークラスはいろいろあるよ

Javaのインナークラスには今回紹介した「staticネストクラス」「非staticネストクラス」の他に、
「無名クラス」と「ローカルクラス」というものもあります。
せっかくなのでちょっと見てみましょうか。

「無名クラス」はインタフェースのインスタンスを直接生成したり、
派生クラスをインスタンス生成時に作成する仕組みです。
Java8でラムダ構文ができたので、今は使うことはあまりないですね。

interface Runnable {
    void run();
}

void test() {
    Runnable runnable = new Runnable() {
        @Override
        public void run() {}
    };
}

「ローカルクラス」はメソッド内で定義するクラスです。
いやほんとこれいつ使うの?

void test() {

    class Local {
    }

    Local local = new Local();
}

上手に使い分けるべし

えーと、みんな違ってみんないいってことでいいですかね?

わざわざ一生懸命使う必要はないですが、
こんな選択肢もあるんだなくらいに心の片隅に置いておくといいことがあるかもしれませんね。

それでは。