【Java SE11 Gold】コンパイルエラーと実行時エラーの見分け方

## 前書き
:::note info
この記事は約5分で読めます
　Javaの基礎を習得している方向けの内容となっております
:::

Java Goldの勉強中に

「これはコンパイルエラーなのか？」「文法ミス以外にも結構コンパイラーでチェックしてくれるんだ！」

とコンパイルエラーのことで理解するのに時間がかかっていたので、今回は

コンパイルエラーと実行時エラーの違いと見分け方について解説していきます

勉強する中で疑問に思ったことや理解できなかったことを記事にして、

他の人や未来の自分が見返せるようにしていきたいと思います。

## 違い
エラーには、大きく分けて2種類あります。

1. コンパイルエラー
1. 実行時エラー

まず最初は、この区別をしっかりつけることが重要になります。

__コンパイルエラー__
>プログラミング言語で記述されたソースコードを機械語のコードに変換（コンパイル）する際に、何らかの致命的な問題が発生して失敗・中断すること。主な原因はプログラムを記述する際の記述ミスによるものが多い

__実行時エラー__
>コンピュータプログラムの実行時に発生するエラーのこと。実行を継続できないほどの致命的な問題であり、プログラムはその時点で実行を中断して直ちに異常終了させられる。コンパイル時にチェックできない。

## 問題
問題を数門出すので考えてみてください。

#### 問題1
次のプログラムを実行した結果として正しいものを選びなさい
```java:Question01
System.out.plintln("hoge");
```
A. コンパイルエラーが発生する
B. 例外がスローされる

#### 問題2
次のプログラムを実行した結果として正しいものを選びなさい
```java:Question02
public class Outer{
    void test(){
        Inner.message = "Hello, Java";
    }

    class Inner{
        private static String message;
        public void test(){
            System.out.println(message);
        }
    }
    
    public static void main(String[] args){
        Outer outer = new Outer();
        outer.test();
        outer.new Inner().test();
    }
}
```
A. コンパイルエラーが発生する
B. 例外がスローされる
C. Hello Javaと表示される

#### 問題3
次のプログラムを実行した結果として正しいものを選びなさい
```java:Question03
public interface A{
    public default void test(){
        System.out.println("A");
    }
}

public abstract class B{
    protected void test(){
        System.out.println("B");
    }
}

public class Sample extends B implements A{
    public static void main(String[] args){
        new Sample().test();
    }
}
```
A. コンパイルエラーが発生する
B. 例外がスローされる
C. Aと表示される
D. Bと表示される


#### 問題４
次のプログラムを実行した結果として正しいものを選びなさい
```java:Question04
import java.util.Optional;

public class FlatMapTest{
    public static void main(String[] args){
        Optional<String> a = Optional.of("test");
        Optional<String> b = a.map(str -> test(str));
        System.out.println(b.get());
    }
    private static Optional<String> test(String str){
        return Optional.of(str.toUpperCase());
    }
}
```
A. コンパイルエラーが発生する
B. 例外がスローされる
C. testと表示される

## 解答
| 問題 | 解答 |
|:-:|:-:|
|  1 |  A |
|  2 |  A |
|  3 |  A |
|  4 |  A |

全てコンパイルエラーでした。笑
全て私が実行しないと分からないのではないか？と迷った問題です。

## 解説
#### 問題1
これは簡単、文法のミスなのでコンパイラーがチェックできます。

#### 問題2
こちらはインナークラスとstaticメンバに関する問題です。
staticではないインナークラスにstaticなフィールドやメソッドは定義できません。
このように、staticなど修飾子が想定内であるかもコンパイラーはチェックできます。

#### 問題3
デフォルトメソッドに関する問題です。
スーパークラスとインターフェースが同一シグニチャのメソッドを持っている場合、スーパークラスのメソッドが優先的に呼び出されます。
設問のコードでは、Sampleクラスが実現しているAインターフェースと継承しているBクラスが同じシグニチャのメソッドを持っています。コンパイラは、SampleクラスはBクラスを引き継いだ上でAインターフェースを実現していると解釈します。そのときBクラスのtestメソッドとAインターフェースのtestメソッドが互換性がないと判断されコンパイルエラーが発生します。
クラスを定義するときの互換性までコンパイラーはチェックできます。

#### 問題4
Optionalインスタンスについての問題です。
Optionalクラスのmapメソッドを使うことで処理結果を持った別のOptionalインスタンスを新たに生成することができます。値ではなくOptionalを戻すmapメソッドを呼び出した場合、Optionalの中にOptionalへの参照が入れ子で入ることになります。そのためmapメソッドはOptional<Optional<String>>型の値を返しますが、戻り値をうけとる変数bの型はOptional<String>型であるため互換性がないとしてコンパイルエラーになります。
一見実行して値が代入されないと気づかないように思えますが、メソッドを呼び出すときに戻り値が決まっているので、型の互換性をチェックすることでコンパイラーはチェックできます。

## まとめ
いかがでしたでしょうか？
全問コンパイルエラーとは想像してなかったのではないでしょうか？笑

このようにコンパイラーは意外と色んなことをチェックして開発者に知らせてくれます。

開発では便利かもしれませんが、少しややこしいので資格勉強者にはとても苦しいです。。。

これをみて少しでも理解が深まった！と思ってくれたら幸いです。