Java 14で最低限押さえておきたい新機能をEclipseで使ってみた

# はじめに
2020/3/17にJava 14がリリースされました。
以下の機能が追加されたようです。
下の表の「本記事の対象」列に〇がついているものを少し触ってみたので後で紹介します。

|JEP（※1） | 概要|本記事の対象|
|---|---|---|
|305 | [Pattern Matching for instanceof (Preview)](https://openjdk.java.net/jeps/305)（※2）|〇|
|343 | [Packaging Tool (Incubator)](https://openjdk.java.net/jeps/343)（※3）|×|
|345 | [NUMA-Aware Memory Allocation for G1](https://openjdk.java.net/jeps/345)|×|
|349 | [JFR Event Streaming](https://openjdk.java.net/jeps/349)|×|
|352 | [Non-Volatile Mapped Byte Buffers](https://openjdk.java.net/jeps/352)|×|
|358 | [Helpful NullPointerExceptions](https://openjdk.java.net/jeps/358)|〇|
|359 | [Records (Preview)](https://openjdk.java.net/jeps/359)|〇|
|361 | [Switch Expressions (Standard)](https://openjdk.java.net/jeps/361)|〇|
|362 | [Deprecate the Solaris and SPARC Ports](https://openjdk.java.net/jeps/362)|×|
|363 | [Remove the Concurrent Mark Sweep (CMS) Garbage Collector](https://openjdk.java.net/jeps/363)|×|
|364 | [ZGC on macOS](https://openjdk.java.net/jeps/364)|×|
|365 | [ZGC on Windows](https://openjdk.java.net/jeps/365)|×|
|366 | [Deprecate the ParallelScavenge + SerialOld GC Combination](https://openjdk.java.net/jeps/366)|×|
|367 | [Remove the Pack200 Tools and API](https://openjdk.java.net/jeps/367)|×|
|368 | [Text Blocks (Second Preview)](https://openjdk.java.net/jeps/368)|〇|
|370 | [Foreign-Memory Access API (Incubator)](https://openjdk.java.net/jeps/370) |×|

公式は[こちら](https://openjdk.java.net/projects/jdk/14/)

※1 JEP
JDK Enhancement Proposalsの略。
JDKに対する変更の提案を管理する番号。

参考：https://www.slideshare.net/AyaEbata/jsrjepjbs
JEPの目次：https://openjdk.java.net/jeps/0

※2 [Preview](https://openjdk.java.net/jeps/12)
プレビュー版のこと。
機能としては保証されているが、どのように使うべきかは議論の余地があるもの。開発者が実際に使ってみて、フィードバックを受けて今後もJava SEの基本機能として提供し続けるかどうかが決まる。

※3 [Incubator](https://openjdk.java.net/jeps/11)
開発者からのフィードバックを得るために、Java SEに含めた実験的なAPI。

参考：https://xtech.nikkei.com/it/atcl/column/15/120700278/080700044/

# Java 14をEclipseで使えるようにするための設定
私はEclipse使いなので、JDK 14のインストールとEclipseの設定から始めます。

## JDKダウンロード
以下のサイトからJDK14をダウンロードします。
https://www.oracle.com/java/technologies/javase-downloads.html

## Eclipseインストール
Eclipseの最新版をインストールします。
https://www.eclipse.org/eclipseide/

## EclipseでJDK14を扱えるようにする
EclipseでJDK14を扱えるようにする手順は以下の通り。

①Eclipse Marketplaceを選択
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254168/7300f1f4-40fc-c3d9-2f1b-191eedf93132.png" width=500>

②Java 14 Support for Eclipse をインストール
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254168/b2e8345e-2084-bca2-5dcc-f15f3e076091.png" width=400>

※こういったEclipse本体やプラグインのリリース情報は公式のtwitterからも流れてくるので興味ある方はフォローすることをお勧めします。

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">🌟 Here is the new me! Eclipse IDE 2020-03! 🌟<br><br>Many improvements for users to leverage in many places<br>📰 <a href="https://t.co/tEp3O53CvA">https://t.co/tEp3O53CvA</a> and <a href="https://t.co/lQsBQecjUb">https://t.co/lQsBQecjUb</a> !<br><br>☕ Support for JDK 14 is also released today, at <a href="https://t.co/bqDu2awnjL">https://t.co/bqDu2awnjL</a><br><br>🙏 Thanks to my contributors!</p>&mdash; 🌒 Eclipse Java IDE (@EclipseJavaIDE) <a href="https://twitter.com/EclipseJavaIDE/status/1240287756586278912?ref_src=twsrc%5Etfw">March 18, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<br>



③Compiler compliance levelを14に設定
※Java 14 Support for Eclipse をインストールしないと14が選択肢に出ません。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254168/e5bb6890-4639-2652-4049-2901dd72b406.png" width=500>

④Installed JREに、インストールしたJDK14を設定
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254168/ad47b305-5b1e-bdb5-ccac-79b36a6070f0.png" width=500>

⑤Execution emvironmentにJDK14を設定
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254168/448b4c4c-cd4b-7403-730f-bc83f3e09c50.png" width=500>

⑥今回はプレビューの機能も使うので、プレビューを有効化します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254168/0f0681f5-d7d2-d3b2-7ddf-4a23c794698d.png" width=500>

これでJava 14を使ったJavaアプリケーションをEclipseで開発・実行できるようになります。

# Java 14の新機能を触ってみる
早速触ってみました。

## [JEP 305：Pattern Matching for instanceof (Preview)](https://openjdk.java.net/jeps/305)
まずはJEP 305のPattern Matching for instanceofです。
instanceof と同時に変数を定義できるようになりました。
実際の書き方の例は以下の通りです。
前半がこれまでの書き方、後半がPattern Matching for instanceofを使用した書き方になっています。

まずはこれまでの書き方。

```java:NotPatternMatchingTest.java
public class NotPatternMatchingTest {
    public static void main(String... args) {
        patternMatch("literal");
        patternMatch(1);

    }

    private static void patternMatch(Object obj) {
        // これまでの書き方
        if (obj instanceof String) {
            String str = (String) obj;
            System.out.println("No Pattern Matching:" + str);
        } else if (obj instanceof Integer) {
            Integer inte = (Integer) obj;
            System.out.println("No Pattern Matching:" + inte);
        }
    }
}
```

これがPattern Matching for instanceof を使用した書き方。

```java:PatternMatchingTest.java
public class PatternMatchingTest {
    public static void main(String... args) {
        patternMatch("literal");
        patternMatch(1);
    }

    // previewなのでSuppressWarningsでワーニング抑止
    @SuppressWarnings("preview")
    private static void patternMatch(Object obj) {
        // Pattern Matching for instanceof
        if (obj instanceof String str) {
            System.out.println("Pattern Matching:" + str);
        } else if (obj instanceof Integer inte) {
            System.out.println("Pattern Matching:" + inte);
        }
    }
}
```


switch文での使用はJava 15以降に持ち越しになっているようです。
http://openjdk.java.net/jeps/8213076


## [JEP 358：Helpful NullPointerExceptions](https://openjdk.java.net/jeps/358)
NullPointerExceptionが発生した場合のメッセージが詳細になり、原因を特定しやすくなりました。
`-XX:+ShowCodeDetailsInExceptionMessages`オプションをつけて実行することで、詳細なメッセージを得ることができます。
以下のサンプルコードで実験してみました。

```java:HelpfulNullPo.java
public class HelpfulNullPo {
    public static void main(String... args) {
        String str = null;
        str.length();
    }
}
```

`-XX:+ShowCodeDetailsInExceptionMessages`オプションを付けずに実行した結果がこちら。

```:実行結果（オプションなし）
Exception in thread "main" java.lang.NullPointerException
	at test.HelpfulNullPo.main(HelpfulNullPo.java:6)
```

`-XX:+ShowCodeDetailsInExceptionMessages`オプションをつけて実行した結果がこちら。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254168/e40836f4-2ea9-aa3e-7d99-794032919d52.png" width=500>


```:実行結果（オプション有）
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "str" is null
	at test.HelpfulNullPo.main(HelpfulNullPo.java:6)

```

NullPointerExceptionの原因特定がしやすくなっています。

なお、JShellの場合は`-R-XX:+ShowCodeDetailsInExceptionMessages`という指定になります。


## [JEP 359：Records (Preview)](https://openjdk.java.net/jeps/359)

データ保持用のクラスとしてrecordという機能がpreviewとして入りました。
以下のように書くだけでデータ保持用のクラスを作ることができます。
（`record Person(String name, int age) {}`の部分）

```java:RecordTest.java
@SuppressWarnings("preview")
record Person(String name, int age) {}

public class RecordTest {
    public static void main(String... args) {
        Person person = new Person("taumax", 13);
        System.out.println("name:" + person.name());
        System.out.println("age:" + person.age());
        System.out.println(person);
    }
}
```

```:実行結果
name:taumax
age:13
Person[name=taumax, age=13]
```

インスタンス生成後に値を変更することはできないようです。
厳密には違いますが、イメージ的にはイミュータブルなJavaBeansを簡単に作ることができるようになったという感覚ですかね。


## [JEP 361：Switch Expressions (Standard)](https://openjdk.java.net/jeps/361)

Switch文がアロー`->`を使って書けるようになり、かなり読みやすくなりました。また、breakの記載も不要になっています。
Switch ExpressionsはJava 12でプレビューとして導入されたものですが、今回のJava 14で正式機能として導入されます。
実際の書き方の例は以下の通りです。
前半がこれまでの書き方、後半がSwitch Expressionsを使用した書き方になっています。

まずはこれまでのswitch文

```java:OldSwitchExpressions
public class OldSwitchExpressions {
    public static void main(String... args) {
        for (Week day : Week.values()) {
            oldSwitch1(day);
        }
    }

    // これまでのswitch文
    private static void oldSwitch1(Week day) {
        System.out.print("old:");
        switch (day) {
            case MONDAY, FRIDAY, SUNDAY:
                System.out.println(6);
                break;
            case TUESDAY:
                System.out.println(7);
                break;
            case THURSDAY, SATURDAY:
                System.out.println(8);
                break;
            case WEDNESDAY:
                System.out.println(9);
                break;
        }
    }

    private enum Week {
        SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
        THURSDAY, FRIDAY, SATURDAY
    }
}
```

これがSwitch Expressions

```java:SwitchExpressions
public class SwitchExpressions {
    public static void main(String... args) {
        for (Week day : Week.values()) {
            newSwitch1(day);
        }
    }

    // 新しいswitch文
    private static void newSwitch1(Week day) {
        System.out.print("new:");
        switch (day) {
            case MONDAY, FRIDAY, SUNDAY -> System.out.println(6);
            case TUESDAY                -> System.out.println(7);
            case THURSDAY, SATURDAY     -> System.out.println(8);
            case WEDNESDAY              -> System.out.println(9);
        }
    }

    private enum Week {
        SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
        THURSDAY, FRIDAY, SATURDAY
    }
}
```

アロー`->`を使い、breakがなくなったことでかなり可読性が高くなったことがわかります。

また、switch文の実行結果をそのまま変数に代入することができるようになりました。
実際の書き方の例は以下の通りです。
前半がこれまでの書き方、後半がSwitch Expressionsを使用した書き方になっています。
まずはこれまでの書き方。

```java:OldSwitchExpressions2
public class OldSwitchExpressions2 {
    public static void main(String... args) {
        for (Week day : Week.values()) {
            oldSwitch2(day);
        }
    }

    // これまでのswitch文
    private static void oldSwitch2(Week day) {
        System.out.print("old:");
        int numLetters;
        switch (day) {
            case MONDAY, FRIDAY, SUNDAY:
                numLetters = 6;
                break;
            case TUESDAY:
                numLetters = 7;
                break;
            case THURSDAY, SATURDAY:
                numLetters = 8;
                break;
            case WEDNESDAY:
                numLetters = 9;
                break;
            default:
                throw new IllegalStateException("Wat: " + day);
        }
        System.out.println(numLetters);
    }

    private enum Week {
        SUNDAY, MONDAY, TUESDAY, WEDNESDAY,  
        THURSDAY, FRIDAY, SATURDAY
    }
}
```

続いてSwitch Expressionsを使用した書き方。

```java:SwitchExpressions2
public class SwitchExpressions2 {
    public static void main(String... args) {
        for (Week day : Week.values()) {
            newSwitch2(day);
        }
    }

    // 新しいswitch文
    private static void newSwitch2(Week day) {
        System.out.print("new:");
        int numLetters = switch (day) {
            case MONDAY, FRIDAY, SUNDAY -> 6;
            case TUESDAY                -> 7;
            case THURSDAY, SATURDAY     -> 8;
            case WEDNESDAY              -> 9;
        };
        System.out.println(numLetters);
    }

    private enum Week {
        SUNDAY, MONDAY, TUESDAY, WEDNESDAY,  
        THURSDAY, FRIDAY, SATURDAY
    }
}
```

これの効果は一目瞭然ですね。素晴らしい！

## [JEP 368：Text Blocks (Second Preview)](https://openjdk.java.net/jeps/368)

改行などを含んだ文字列を定義できるようになりました。
文字列を定義する場合の`"`ではなく`"""`で囲みます。
開始の`"""`のあとには文字列を続けることができません。

```java:TextBlocks
public class TextBlocks {
    @SuppressWarnings("preview")
    public static void main(String... args) {
        // これまでは改行をエスケープする必要があった。
        String str1 = "I have a pen.\n"
                + "I don't have a pen.";
        System.out.println(str1);

        // エスケープしなくても改行してくれる。
        String str2 = """
                I have a pen.
                I don't have a pen.
                """;
        System.out.println(str2);

        // 改行したくない場合は「\」を付ける。
        String str3 = """
                I have a pen.\
                I don't have a pen.\
                """;
        System.out.println(str3);

        // 文字列に変数を埋め込む場合
        String str4 = """
                Hello %s.
                """.formatted("taumax");
        System.out.println(str4);
    }
}
```

```:実行結果
I have a pen.
I don't have a pen.

I have a pen.
I don't have a pen.


I have a pen.I don't have a pen.

Hello taumax.

```

以上。

# 参考
・[Oracle、「Java 14」を発表 ～Recordクラスがプレビュー導入、switch式が正式機能に](https://forest.watch.impress.co.jp/docs/news/1241637.html)
・[Java 14がリリース](https://news.mynavi.jp/article/20200320-999787/)