【10分で分かる】Java8からJava15まで

本記事は、[Philipp Gysel](https://philipp-gysel.medium.com/)氏による「[From Java 8 to Java 15 in Ten Minutes](https://medium.com/swlh/from-java-8-to-java-15-in-ten-minutes-f42d422a581e)」（2021年1月18日公開）の和訳を、著者の許可を得て掲載しているものです。

#【10分で分かる】Java8からJava15まで

![Image for post](https://miro.medium.com/max/700/1*DVBOCJfWxma4DL7HHvTV3w.png)

>Java 8からJava 15までの道のりには、朗報がたくさんだ。

##はじめに

この記事ではJava 7以降に追加された素晴らしい新機能を紹介します。2020年秋にリリースされたJava 15まで、Javaのバージョン毎に大きな改善点を見ていきます。Javaは今や、**ラムダ式**、**関数型プログラミング**、`var`による型推論、単純なコンストラクタによる**不変コレクション**、**複数行文字列**を、完全にサポートするようになりました。さらに、**データクラス**（`record`）や`sealed`クラスなど、新しくて面白い実験的な機能があります。そして、時間対効果の高い**Java REPL**について説明します。

##1. 関数型プログラミング (Java 8)

Java 8は、関数型プログラミングとラムダ式を言語機能として追加しました。[関数型プログラミング](https://www.freecodecamp.org/news/an-introduction-to-the-basic-principles-of-functional-programming-a2c2a15c84/)の2つのコアパラダイムは、**不変の値**と**関数**の重要性向上です。データは変換処理のパイプラインを通過します。各ステップで入力を受け取り、それを新しい出力にマッピングします。関数型プログラミングは、以下に示すように、Javaの`Stream`とnull安全モナド（`Optional`）で使えます。

##2. Streams (Java 8)

一般的なコンピュータプログラムでは、値のリストを操作して、**各値に対して所定の変換**を実行することがよくあります。Java 8以前は、この変換に`for`**ループ**を使う必要がありましたが、これからは次のように[Stream](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html)を使えます。

```java
Stream.of("hello", "great")
	.map(s -> s + " world")
	.forEach(System.out::println);
> hello world
> great world
```

[Streams.java](https://gist.github.com/gyselph/6e58f18cb2a6945dc2e7b3d8c015481c#file-streams-java)

`map`関数は、stream内のすべての要素に適用されるラムダを入力として受け取ります。

`Stream`は（変換を介して）`List`、`Set`、`Map`で動作します。Streamのおかげで、コードのループをほとんどなくせます！👌

##3. Optional (Java 8)

Javaでよくあるもう1つの問題は、**Nullポインタ例外**でした。そこで Javaは[Optional](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html)を導入しました。これは、Nullの場合もNullでない場合も、参照をラップするモナドです。Optionalにアップデートするには、関数の形を使います。

```java
Optional.of(new Random().nextInt(10))
	.filter(i -> i % 2 == 0)
	.map(i -> "number is even: " + i)
	.ifPresent(System.out::println);
> number is even: 6
```

[Optional.java](https://gist.github.com/gyselph/2fb0a3f6dddf0bf728e08eff821f9d90#file-optional-java)

上の例では、乱数を作成し、それをOptionalオブジェクトでラップし、偶数の場合にのみ値を出力しています。

##4. JShell (Java 9)

いよいよJavaの[REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop)、[JShell](https://docs.oracle.com/javase/9/jshell/introduction-jshell.htm#JSHEL-GUID-630F27C8-1195-4989-9F6B-2C51D46F52C8)です！😊簡単に言うと、JShellを使えば、Javaの完全なクラスを書いてコンパイルしなくても、スニペットを試せます。**1度に1つのコマンドを実行して**、すぐに結果を見られます。簡単な例はこちら。

```java
$ <JDK>/bin/jshell
jshell> System.out.println("hello world")
hello world
```

[JShell.sh](https://gist.github.com/gyselph/ea851344c5a20bfae239650ce1155941#file-jshell-sh)

JavaScriptやPythonのようなインタプリタ型言語に精通している人は、長い間REPLを楽しんできましたが、Javaには今までこの機能がありませんでした。JShellでは**変数**だけでなく、**複数行の関数**、クラス、**ループ**の実行など、より複雑なエンティティも定義できます。さらに、オートコンプリートもサポートしており、所定のJavaクラスが提供する正確なメソッドを知らない場合に便利です。

##5. 不変コレクションのファクトリメソッド (Java 9)

`List`の単純な初期化は、Javaでは長い間欠落していましたが、そんな時代は終わりました。😅以前はこんなことをしなければなりませんでした。

```java
jshell> List<Integer> list = Arrays.asList(1, 2, 3, 4)
list ==> [1, 2, 3, 4]
```

[CollectionsOld.sh](https://gist.github.com/gyselph/5199afa77aa231e96acf32ed03019186#file-collectionsold-sh)

これが次のように簡略化されました。

```java
jshell> List<Integer> list = List.of(1, 2, 3, 4)
b ==> [1, 2, 3, 4]
```

[CollectionsNew.sh](https://gist.github.com/gyselph/ed12f3ae8b1e19333431536d0d3c1f80#file-collectionsnew-sh)

このしゃれた`of(...)`メソッドは[List](https://docs.oracle.com/javase/9/docs/api/java/util/List.html)、[Set](https://docs.oracle.com/javase/9/docs/api/java/util/Set.html)、[Map](https://docs.oracle.com/javase/9/docs/api/java/util/Map.html)にあります。どれも、たった1行のシンプルなコードで不変オブジェクトを作成してくれます。

##6. `var`による型推論 (Java 10)

Java 10では、変数の**型を省略**できる新しい[var](https://developer.oracle.com/java/jdk-10-local-variable-type-inference.html)キーワードを導入しました。

```java
jshell> var x = new HashSet<String>()
x ==> []

jshell> x.add("apple")
$1 ==> true
```

[TypeInference.sh](https://gist.github.com/gyselph/825665ae1abd0f4734276271d49ca740#file-typeinference-sh)

上の例では、コンパイラによって `x`の型が HashSet と**推測**できます。

この機能は、**ボイラープレートコードを減らし**、**可読性を向上させる**のに便利です。でも、いくつかの制限があります。`var`はメソッド本体の中でしか使用できません。コンパイラはコンパイル時に型を推測するため、すべて静的に型付けされています。

##7. 単一ソースファイルの起動 (Java 11)

以前は、ファイルが1つのシンプルなJavaプログラムを書いたら、まずそのファイルを`javac`でコンパイルしてから`java`で実行しなければなりませんでした。Java 11では、1つのコマンドで両方のステップを実行できます。

まず、単一ソースファイル`Main.java`を定義します。

```java
public class Main {
	public static void main(String[] args) {
		System.out.println("hello world");
	}
}
```

[SingleSourceLaunch.java](https://gist.github.com/gyselph/8dced9080f797ca46beacf86e7e1c956#file-singlesourcelaunch-java)

これで、1ステップでコンパイルと実行ができるようになりました。

```java
$ java ./Main.java
hello world
```

[SingleSourceLaunch.sh](https://gist.github.com/gyselph/62c69c0ee07c09c8ad74640efa7dc95c#file-singlesourcelaunch-sh)

シンプルなスタータープログラムや1つのJavaクラスだけのテストの場合、この機能で[単一ソースファイルを起動する](https://www.baeldung.com/java-single-file-source-code)作業がもっと楽になります。

##8. Switch式 (Java 12)

Java 12では、[Switch式](https://docs.oracle.com/en/java/javase/13/language/switch-expressions.html)が登場しました。ここでは、この式が古いswitch文とどう違うかを簡単に紹介します。

**古いswitch文**はプログラムの流れを定義します。

```java
jshell> var i = 3
jshell> String s;
jshell> switch(i) {
	...>	case 1: s = "one"; break;
	...>	case 2: s = "two"; break;
	...>	case 3: s = "three"; break;
	...>	default: s = "unknown number";
	...> }
jshell> s
s ==> "three"
```

[SwitchOld.sh](https://gist.github.com/gyselph/479ab90cd2d8651fd1f4b099786ab119#file-switchold-sh)

対照的に、**新しいSwitch式**は値を返します。

```java
jshell> var i = 3;
jshell> var x = switch(i) {
	...>	case 1 -> "one";
	...>	case 2 -> "two";
	...>	case 3 -> "three";
	...>	default -> "unknown number";
	...> };
x ==> "three"
```

[SwitchNew.sh](https://gist.github.com/gyselph/9c0071ca89a5960bd2243d17c6165db7#file-switchnew-sh)

この新しいswitch式は一種の**マッピング**関数であることに注目してください。1つの入力（ここでは`i`）と1つの出力（ここでは`x`）があります。これは実は[パターンマッチング](https://en.wikipedia.org/wiki/Pattern_matching)機能であり、Javaを**関数型プログラミング**の原則とより互換性のあるものにするのに便利です。[同様のswitch文](https://docs.scala-lang.org/tour/pattern-matching.html)はしばらくScalaでも使えました。

注意すべき点がいくつかあります。

-   コロンの代わりに、矢印`->`を使っています。

-   `break`は必要ありません。

-   考え得るすべてのcaseを考慮すれば、defaultは省略できます。

-   Java 12でこの機能を有効にするには、`--enable-preview -source 12`を使用します。

##9. 複数行文字列 (Java 13)

JSONやXMLのような長い**複数行文字列**を定義したことはありますか？これまでは、すべてを1行にまとめて改行文字`\n`を使うことが多かったと思いますが、これでは文字列が読みにくくなってしまいます。Java 13では[複数行文字列](https://blogs.oracle.com/javamagazine/text-blocks-come-to-java)が使えるようになりました！💪

例：

```java
public class Main {
	public static void main(String [] args) {
		var s = """
			{
				"recipe": "watermelon smoothie",
				"duration": "10 mins",
				"items": ["watermelon", "lemon", "parsley"]
			}""";
		System.out.println(s);
	}
}
```

[MultiLineString.java](https://gist.github.com/gyselph/cebd2f0d2eb2f5bd258b6c640c9c40a7#file-multilinestring-java)

さて、単一ファイル起動でMainメソッドを実行してみましょう。

```java
java --enable-preview --source 13 Main.java
{
	"recipe": "watermelon smoothie",
	"duration": "10 mins",
	"items": ["watermelon", "lemon", "parsley"]
}
```

[MultiLineString.sh](https://gist.github.com/gyselph/500dc86111a4b4f1133c150d83d5a351#file-multilinestring-sh)

結果として得られるStringは複数行にまたがり、引用符`""`はそのままで、タブ`\t`も保持しています！

##10. データクラス`record` (Java 14)

この記事の新機能すべての中で、私が最も興奮しているのはたぶんこれです。ついにJavaに**データクラス**が登場しました！このクラスは[record](https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/lang/Record.html)キーワードで宣言され、自動Getter、コンストラクタ、equalsメソッドなどがあります。要するに、膨大なボイラープレートコードの塊を取り除くことができるのです！🙌🎉

```java
jshell> record Employee (String name, int age, String department) {}
| created record Employee

jshell> var x = new Employee("Anne", 25, "Legal");
x ==> Employee[name=Anne, age=25, department=Legal]

jshell> x.name()
$2 ==> "Anne"
```

[Record.sh](https://gist.github.com/gyselph/e475e9d7c4d6dfcc59c3e2bd4cb7808a#file-record-sh)

Scalaには同様の[ケースクラス](https://docs.scala-lang.org/tour/case-classes.html)、Kotlinには[データクラス](https://kotlinlang.org/docs/reference/data-classes.html)があります。Javaでは多くの開発者が、Java 14の`record`に影響を与えた機能のほとんどを提供する[Lombok](https://projectlombok.org/)を使っていました。詳細については[Baeldungの記事](https://www.baeldung.com/java-record-keyword)を見てください。

##11. キャストなし`instanceof` (Java 14)

`instanceof`キーワードは、すでにJava の以前のバージョンでありました。

```java
Object obj = new String("hello");
if (obj instanceof String) {
	System.out.println("String length: " + ((String)obj).length());
}
```

[InstanceOfOld.java](https://gist.github.com/gyselph/5c111577ffb706c65bd1c3b49bdb72cd#file-instanceofold-java)

残念な部分は、まず、`obj`が`String`型であることを確認してから、もう一度キャストして長さを取得するところです。

Java 14では、コンパイラが[instanceofチェック後に自動で型推論する](https://blogs.oracle.com/javamagazine/pattern-matching-for-instanceof-in-java-14)ようになりました。

```java
Object obj = new String("hello");
if (obj instanceof String mystr) {
	System.out.println("String length: " + mystr.length());
}
```

[InstanceOfNew.java](https://gist.github.com/gyselph/311cf7bdf0bf030fac1c6fce21c9fb75#file-instanceofnew-java)

##12. Sealedクラス (Java 15)

[sealed キーワード](https://docs.oracle.com/en/java/javase/15/language/sealed-classes-and-interfaces.html)で、所定のクラスやインターフェイスを**拡張**できるクラスを**制限**できます。

例：

```java
public sealed interface Fruit permits Apple, Pear {
	String getName();
}

public final class Apple implements Fruit {
	public String getName() { return "Apple"; }
}

public final class Pear implements Fruit {
	public String getName() { return "Pear"; }
}
```

[SealedClass.java](https://gist.github.com/gyselph/6f22547a7dbb86801e51882eed9787b3#file-sealedclass-java)

これがどのように役立つのでしょうか？`Fruit`が何個あるかわかりましたね。これは、[完全にサポートされたパターンマッチング](https://blogs.oracle.com/javamagazine/inside-the-language-sealed-types)への重要な一歩です。列挙型のようにクラスを扱うことができます。この`sealed`機能は、上で説明した新しい`switch`式と相性が良いです。

##おまけ：Java 8以降のライセンス条項の更新

この記事の最後の話題は、**ライセンス**です。[OracleがJava 8（無料の商用版）のアップデートを停止した](https://www.oracle.com/java/technologies/java-se-support-roadmap.html)ことを聞いたことがあると思います。そこで、次のような選択肢があります。

-   **新しいOracle JDK**バージョンを使用する（Oracleは各リリース後6ヶ月間だけ無料のセキュリティアップデートを提供）。

-   自己責任で**古いJDK**バージョンを使用する。

-   古い**OpenJDK**のJavaバージョンを使用する。これは引き続き、オープンソースコミュニティやサードパーティベンダーからセキュリティアップデートを取得する。

-   **プレミアムサポート**のためOracleに支払う（例：Java 8は2030年までサポート）。

次は、JDKごとの暫定的なOracleサポート期間です。

![Image for post](https://miro.medium.com/max/625/1*CMcZQPLx5_7hrXqePmLPKw.png)
>JDKごとのOracleサポート予定

Oracleの新しいライセンスモデルは、新しい[リリースサイクル](https://blogs.oracle.com/java-platform-group/update-and-faq-on-the-java-se-release-cadence)の影響を受けます。Oracleは6ヶ月ごとに新しいJavaバージョンを発表します。これにより、Oracleはより速いペースでJavaを改善し、実験的な機能を通じてより迅速なフィードバックを取得し、[Scala](https://www.scala-lang.org/)、[Kotlin](https://kotlinlang.org/)、[Python](https://www.python.org/)などの最新の言語に追いつくことができます。

ライセンスの詳細については、[この記事](https://medium.com/@javachampions/java-is-still-free-2-0-0-6b9aa8d6d244)を見てください。

##まとめ

Javaはこの6年間で長い道のりを歩んできて、新しいJavaリリースが8つありました！🚀この素晴らしい新機能はすべて、他のJVMベースのライバル（ScalaやKotlin）と比べて、Javaを競争力のあるオプションにするのに役立ちます。

この記事を書くのはとても楽しかったです🔥 読んでくれてありがとう😊最近のJavaリリースでお気に入りの機能は何ですか？感想を待っています！

[DEV.to](https://dev.to/pmgysel/from-java-8-to-java-15-in-ten-minutes-22f7)で初公開。

##翻訳協力

この記事は以下の方々のご協力により公開する事ができました。改めて感謝致します。

Original Author: [Philipp Gysel](https://philipp-gysel.medium.com/)
Original Article: [From Java 8 to Java 15 in Ten Minutes](https://medium.com/swlh/from-java-8-to-java-15-in-ten-minutes-f42d422a581e)
Thank you for letting us share your knowledge!

選定担当: @gracen
翻訳担当: @gracen
監査担当: -
公開担当: @gracen

##ご意見・ご感想をお待ちしております
今回の記事はいかがでしたか？
・こういう記事が読みたい
・こういうところが良かった
・こうした方が良いのではないか
などなど、率直なご意見を募集しております。
頂いたお声は、今後の記事の質向上に役立たせて頂きますので、お気軽に
コメント欄にてご投稿ください。[Twitter](https://twitter.com/BabyDegu?lang=ja)でもご意見を受け付けております。
皆様のメッセージをお待ちしております。