Java 変数のスコープ(変数を見れる範囲)

##はじめに
この記事はこれからJavaを勉強する方に向けた私自身の理解している内容の記録です。

私の記事では下記のようにJavaの主要な機能を下記リストにまとめています。今回は変数と型についてはある程度把握している前提なので、わからない箇所があれば、下記リンクから確認していただければと思います

・[変数と型、型変換](https://qiita.com/pitan109/items/2538b234a2c6e20c037f)
・変数のスコープ 現在ページ
・文字列の操作（準備中）
・配列の操作（準備中）
・演算子（準備中）
・条件分岐（準備中）
・繰り返し処理（準備中）
・[例外処理](https://qiita.com/pitan109/items/c9910edddc007126df41)
・クラスについて（準備中）
・抽象クラス（準備中）
・インターフェース（準備中）
・カプセル化（準備中）
・モジュールについて（準備中）

## 変数のスコープ
[Javaの変数宣言や初期化、型について](https://qiita.com/pitan109/items/2538b234a2c6e20c037f)
上記リンクは以前あげた私の記事ですが、宣言する時や、変数内を参照する時に気をつけなければならないことが他にもあります。それは変数を見れる範囲です。下記ではローカル変数、static変数を例に使用しています。

```java
class Main{
    public static void main(String args[]){
        int i = 30;
        
        System.out.println(i);
    }
}
```

mainメソッド内で int iを宣言しています。なのでmainの処理が終わるまで、mainの中で使えます。実行すると

```java
30
```

が表示されているので、iの中身を表示できています。ただ宣言する場所を限定してしまうと、その変数を見つけられない場合があります。

```java
class Main{
    public static void main(String args[]){
        if(true){
            int i = 30; // ifの中で宣言
        }
        
        System.out.println(i);
    }
}
```

この記載を実行すると、下記のようなエラーが出ます。

```
エラー: シンボルを見つけられません
        System.out.println(i);
                           ^
```

if内で宣言しているので、iが見つからないんですね。
こういったことが無いように広い範囲で宣言してやれば、このようなミスはなくなるのでは無いかと思いますが、このやり方は落とし穴があります。

```java
class Main{
    static int i = 30;
    public static void main(String args[]){
        System.out.println(i);
        plus20();
    }

    static void plus20(){
        i = i + 20;
        System.out.println(i);
    }
}
```

上記を実行すると、

```java
30
50
```

になります。iをclass内ならどこでも使えるよう宣言しているので、plus20でもmainでも変数内を取得できていますね。
この記載でいいように思えますが、例えばplus20メソッドは20を足した状態を表示したいだけのメソッドであり、後で元の値を表示したくなった時、iの値はplus20で書き換わってしまいます。

```java
class Main{
    static int i = 30;
    public static void main(String args[]){
        System.out.println(i);
        plus20();
        System.out.println(i);
    }

    static void plus20(){
        i = i + 20;
        System.out.println(i);
    }
}

```

実行すると

```
30
50
50
```

コードの順番では、3つ目の数値は30であって欲しいはずですが、plus20の中で値が書き換わっているため、50になっています。
こういったトラブルが起こるため、変数の範囲はできるだけ絞った方がいいと思います。

```java
class Main{
    public static void main(String args[]){
        int i = 30;
        System.out.println(i);
        plus20(i);
        System.out.println(i);
    }

    static void plus20(int z){
        z = z + 20;
        System.out.println(z);
    }
}
```

実行すると

```
30
50
30
```

plus20内で数値を足していても、iに干渉していないので、iの元の数値が出せました。

上記で出ていた変数の参照できる範囲によってそれぞれ名前があります。

##ローカル変数
基本的にはメソッド内で使われる変数で、メソッド外では参照できないようにしているのがローカル変数です。

```java
class Main{
    public static void main(String args[]){
        int i = 100; //ここから


        System.out.println(i);
        //ここまで
    }
}
```

基本は変数の参照範囲を狭めたいので、この記載にはなりやすいと思います。

## インスタンス変数
基本はクラスの下に宣言を記載し、クラス内で扱います

```java
class Mikan{
    int i;

    Mikan(){
        this.i = 10;
    }
}
```
Mikanのインスタンス生成時にそれぞれのインスタンスに宣言されます。Mikanを2つ作ればそれぞれがint iを持っているような感じです。この定義の場合は、インスタンス内なら使用できる変数となり、外部でインスタンスを生成して変数を読み込んだりもできます。

```java
class Mikan{
    int j;
    Mikan(){
        this.j = 10;
    }

}

class Main{
    public static void main(String args[]){
        Mikan mikan1 = new Mikan();
        Mikan mikan2 = new Mikan();

        mikan2.j += 10;

        // インスタンス変数
        System.out.println(mikan1.j);
        System.out.println(mikan2.j);
    }
}

```

ここで実行すると

```
10
20
```

と表示されます。Mainクラス内でMikanインスタンスを作成し、その中の変数を参照できています。

##static変数(クラス変数)
static変数は先ほどのインスタンス変数に似ていますが、こちらはインスタンスを生成しなくても使用できます。

```java
class Mikan{
    static int i = 10;

}

class Main{
    public static void main(String args[]){
        System.out.println(Mikan.i);
    }
}
```

```
10
```

インスタンス変数に似ているように見えますが、インスタンス変数は生成されるごとに変数を持ちます。static変数はクラス内で同じ値を共有します。

```java
class Mikan{
    static int i = 10;
    int j;
    Mikan(){
        this.j = 10;
    }

}

class Main{
    public static void main(String args[]){
        Mikan mikan1 = new Mikan();
        Mikan mikan2 = new Mikan();

        mikan2.j += 10;

        // インスタンス変数
        System.out.println(mikan1.j);
        System.out.println(mikan2.j);

        // static変数
        
        System.out.println(Mikan.i);

        Mikan.i += 30;

        System.out.println(Mikan.i);
    }
}
```

```
10
20
10
40
```

インスタンスごとの独自の値を持ちたい時はインスタンス変数、共通で1つの変数を見たい時はstatic変数といった感じで使い分けていきましょう。

また、インスタンス変数やstatic変数をクラス内の関数から呼び出すときは、このような記載になります。

```java
class Mikan{
    static int i = 10;
    int j = 50;

    void inscheck(){
        // static変数は呼び出せる
        System.out.println(i);

        // インスタンス変数は呼び出せる
        System.out.println(j);
    }

    static void check(){
        // static変数は呼び出せる
        System.out.println(i);

        // インスタンス変数は呼び出せない
        // System.out.println(j);
    }
}

class Main{
    public static void main(String args[]){
        Mikan mikan1 = new Mikan();

        // インスタンスが持っている関数
        mikan1.inscheck();

        // static関数
        Mikan.check();
    }
}
```
実行すると

```
10
50
10
```

となります。インスタンスから呼び出したインスタンスの関数からだと、両方とも呼び出せます。

クラスから呼び出したstatic関数の場合はstatic変数は呼び出せますが、インスタンス変数は呼び出せません。

コメントアウトを外してコンパイルすると

```
test.java:18: エラー: staticでない変数 jをstaticコンテキストから参照することはできません
        System.out.println(j);
                           ^
エラー1個
```

とでます。このようなエラーになったときは、変数指定の修飾子を確認するようにすれば早く解決できると思います。


##終わりに
変数が見れる範囲についての解説でした。この辺りの理解が進むと、値が意図していない物になっている等のバグは少なくなると思います。