javaの内部クラスおさらい

「javaの内部クラス」、
意外と知ってそうで知らない箇所が多かったので、おさらいしてみようと。

# 内部クラスとは
言葉の通り、クラス内部のクラスである。

## 内部クラスの用途
用途としては、もっと良い隠蔽(カプセル化)を提供する。
外部クラスを離れては意味のないクラスを内部に隠蔽することができる。
例：Cow.javaとCowLeg.java

## 内部クラスの分類
1. 非static内部クラスは、__外部クラスのインスタンスに関連__する
2. static内部クラスは、__外部クラスに関連に関連__する
3. local内部クラスは、__メソッドに関連__する

どれに関連するかは非常に大事な内容であり、
それによって、使い方が決まっていたことが分かった。

# 非static内部クラスの特徴
外部クラスの非staticのフィールド、メソッド、コンストラクター等と同じ扱いである。
⇒ インスタンスが存在しないと使えないもの。

## classでありながら、private,protectedが使える
(staticも付けれるが、static内部クラスになるので後で)

```java:private,protected可視性
public class Outer {
    public class PublicInner {}
    private class PrivateInner {}
    public class PackagePrivateInner {}
}
```

1. classの上位単位はpackageであり、public、package privateで十分
1. 非static内部クラスの上位単位は外部クラスなので、すべての可視性が使える

## 内部は外部が見えるが、外部は内部が見えない
```java:可視性
public class Outer {
    private int outerVal;

    public class Inner {
        public int innerVal;
        private void method() {
            // OK
            this.innerVal = Outer.this.outerVal;
        }
    }

    private void method() {
        // NG
        // this.outerVal = innerVal;
    }
}
```
非static内部クラスは、__外部クラスのインスタンスに関連している__ので、  
1. 「非static内部クラスインスタンスが存在する = 外部クラスインスタンスが存在する」
⇒ 内部から外部が見える
2. 「外部クラスインスタンスが存在する キ 非static内部クラスインスタンスが存在する」
⇒ 外部から内部が見えない

※ __非static内部クラスを使い場合は、明示的なインスタンス生成が必要。__

## 非static内部クラスのインスタンス生成
```java:非static内部クラスのインスタンス生成
Outer.Inner inner = (new Outer()).new Inner();
```
1. (new Outer()) -> 外部クラスのインスタンス
2. 外部クラス.new Inner() -> 外部クラスのインスタンス内に非static内部クラスのインスタンス生成

## 非static内部クラスのインスタンスは外部クラスから丸見え

```java:外部クラスからアクセス
public class Outer {
    private int outerVal;

    public class Inner {
        private int privateA = 1;
        protected  int protectedB = 2;
        public int publicC = 3;
    }

    private void method() {
        Inner inner = new Inner();

        System.out.println(inner.publicC);
        System.out.println(inner.protectedB);
        System.out.println(inner.privateA);
    }
}
```

```java:他のクラスからアクセス
public static void main(String[] args) {
    Outer.Inner inner = new Outer().new Inner();
    System.out.println(inner.publicC);
    System.out.println(inner.protectedB);
    
    // NG
    // System.out.println(inner.privateA);
}
```
内部クラスのインスタンスは、
1. 外部クラスからはprivateまで見える
2. 他のクラスからは可視性が働く

__OuterからInnerのprivateが見える理由：__
![inner-private.png](https://qiita-image-store.s3.amazonaws.com/0/104977/38bc8b17-6204-ebbc-ff11-2d7b79f0e1eb.png)

Outer$Inner@461
Innerは独立しているわけではなくて、Outerの一部だと理解した。
よって、同じクラス内なので、InnerのPrivateもOuterで見える。
static内部クラスも同じだった。

__もっといい解釈を求めてます！！！__

## staticなfield,method,blockは使えない

コンパイルエラー：
「Inner classes cannot have static declarations」 

```java:staticメンバーはＮＧ
public class Outer2 {
    public class Inner {
        static { /** something **/ }
        public static int staticValue;
        public static void staticMethod() { /** something **/ }
    }
}
```
非static内部クラスは、__外部クラスのインスタンスに関連している__ので、
1. staticは、インスタンスではなくクラスに関連付けるためのものなので、  
文法からみて矛盾しているので、コンパイルエラー

## 変数の優先順位
```java:変数の優先順位
public class Outer {
    String a = "outer-a";
    String b = "outer-b";
    String c ="outer-c";

    public class Inner {
        String a = "inner-a";
        String b = "inner-b";
        private void method() {
            String a = "method-a";

            System.out.println(a);
            System.out.println(b);
            System.out.println(c);

            System.out.println(this.a);
            System.out.println(Outer.this.a);
        }
    }

    public static void main(String[] args) {
        new Outer().new Inner().method();
    }
}
```
```
method-a
inner-b
outer-c
inner-a
outer-a
```
非static内部クラスの優先順位は
1. メソッド内local変数
2. 内部クラスのインスタンス変数
3. 外部クラスのインスタンス変数

__this.変数__, __Outer.this.変数__で変数範囲を指定できる。
![var-priority.png](https://qiita-image-store.s3.amazonaws.com/0/104977/7d8d9056-bc75-8f92-ce79-4866e319a54f.png)
このデバックした図から分かるね！！


# static内部クラスの特徴
ぶっちゃけ、通常のクラスと変わりません！！
が、内部クラスとしても特徴はあります。

## classでありながら、private,protectedが使える
非static内部クラスと同じく、上位はクラスであるので、private,protectedが使える。

## static内部クラスのインスタンスは外部クラスから丸見え
非static内部クラスと同じく、外部クラスへ所属しているので。

## static内部クラスのインスタンス生成
```java:static内部クラスのインスタンス生成
Outer.Inner inner = new Outer.Inner();
```
1. new 外部クラス.staticな内部クラス()で生成できる
※ 外部クラスがパッケージっぽくなってますね。

# local内部クラス

```java:
public static void main(String[] args) {
    class Inner {
        protected int a = 1;
    }
    class InnerSub extends Inner {
        private int b = 2;
    }
    InnerSub sub = new InnerSub();
    System.out.println(sub.a + sub.b);
}
```
1. local変数にprivate,protectedが不要と同じく、__可視性は使えない__
2. メソッドに関連しているので、__staticは使えない__

# まとめ
1. 内部クラスを使う際には、__「何(クラス、インスタンス、メソッド)と関連させたいのか」__を明確にするのが重要だ。
2. 原則として内部クラスはstaticにすることが推奨されます。
主な理由は、staticでない内部クラスが暗黙的にエンクロージングクラス（トップレベルクラス）のインスタンスを参照するためです。(セキュリティ面？？)「this$0」

![inner-private.png](https://qiita-image-store.s3.amazonaws.com/0/104977/38bc8b17-6204-ebbc-ff11-2d7b79f0e1eb.png)

From Effective Java 第2版 (The Java Series)