java.util.Arrays.asList()はnew ArrayList<>()の代替ではない


今回は「`java.util.Arrays.asList()`は`new ArrayList<>()`の代替ではない」という話について説明したいと思います。
いきなりですが例題です。

```java:before
List<String> fruts = new ArrayList<>();
fruts.add("apple");
fruts.add("orange");
fruts.add("strawberry");
```
を

```java:after
List<String> fruts = Arrays.asList("apple", "orange", "strawberry");
```

**短くなるからと改修させるレビュアーは危険です。**

```java:このインターフェースなら全部動くと思いますか？
void show(List<String> fruts);
```

コレクションとして操作しているなら問題ありませんが、インターフェースの実装によってはエラーとなる場合があるからです。

**インスタンスの型を見れば分かりますが`java.util.Arrays.asList()`で作成されるインスタンスは`java.util.ArrayList`ではなく`java.util.Arrays$ArrayList`です。**
問題が発生するとしたらこの違いに関するところになります。
以下のデモアプリで確認してみましょう。

```java:デモアプリ
package com.example;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Demo {

	public static void main(String[] args) {
		List<String> fruts1 = new ArrayList<>();
		fruts1.add("apple");
		fruts1.add("orange");
		fruts1.add("strawberry");
		show(fruts1);
		System.out.println("------------------------------------------------------------");
		List<String> fruts2 = Arrays.asList("apple", "orange", "strawberry");
		show(fruts2);
	}

	private static void show(List<String> fruts) {
		System.out.println(fruts + " is " + fruts.getClass().toString());
		if (java.util.ArrayList.class.isAssignableFrom(fruts.getClass())) {
			System.out.println("OK : java.util.ArrayList.class.isAssignableFrom(fruts.getClass())");
		} else {
			System.out.println("NG : java.util.ArrayList.class.isAssignableFrom(fruts.getClass())");
		}
		if (java.util.List.class.isAssignableFrom(fruts.getClass())) {
			System.out.println("OK : java.util.List.class.isAssignableFrom(fruts.getClass())");
		} else {
			System.out.println("NG : java.util.List.class.isAssignableFrom(fruts.getClass())");
		}
		if (java.util.ArrayList.class.isInstance(fruts)) {
			System.out.println("OK : java.util.ArrayList.class.isInstance(fruts)");
		} else {
			System.out.println("NG : java.util.ArrayList.class.isInstance(fruts)");
		}
		if (java.util.List.class.isInstance(fruts)) {
			System.out.println("OK : java.util.List.class.isInstance(fruts)");
		} else {
			System.out.println("NG : java.util.List.class.isInstance(fruts)");
		}
		if (fruts instanceof java.util.List) {
			System.out.println("OK : fruts instanceof java.util.List");
		} else {
			System.out.println("NG : fruts instanceof java.util.List");
		}
		if (fruts instanceof java.util.ArrayList) {
			System.out.println("OK : fruts instanceof java.util.ArrayList");
		} else {
			System.out.println("NG : fruts instanceof java.util.ArrayList");
		}
		try {
			List<String> castObj = (List<String>) fruts;
			System.out.println("OK : List<String> castObj = (List<String>) fruts;");
		} catch(Exception e) {
			e.printStackTrace();
		}
		try {
			List<String> castObj = (ArrayList<String>) fruts;
			System.out.println("OK : List<String> castObj = (ArrayList<String>) fruts");
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
}
```

```console:動作結果
[apple, orange, strawberry] is class java.util.ArrayList
OK : java.util.ArrayList.class.isAssignableFrom(fruts.getClass())
OK : java.util.List.class.isAssignableFrom(fruts.getClass())
OK : java.util.ArrayList.class.isInstance(fruts)
OK : java.util.List.class.isInstance(fruts)
OK : fruts instanceof java.util.List
OK : fruts instanceof java.util.ArrayList
OK : List<String> castObj = (List<String>) fruts;
OK : List<String> castObj = (ArrayList<String>) fruts
------------------------------------------------------------
[apple, orange, strawberry] is class java.util.Arrays$ArrayList
NG : java.util.ArrayList.class.isAssignableFrom(fruts.getClass())
OK : java.util.List.class.isAssignableFrom(fruts.getClass())
NG : java.util.ArrayList.class.isInstance(fruts)
OK : java.util.List.class.isInstance(fruts)
OK : fruts instanceof java.util.List
NG : fruts instanceof java.util.ArrayList
OK : List<String> castObj = (List<String>) fruts;
java.lang.ClassCastException: java.util.Arrays$ArrayList cannot be cast to java.util.ArrayList
	at com.example.Demo.show(Demo.java:59)
	at com.example.Demo.main(Demo.java:17)
```

一応インターフェースで隠ぺいされているので、`Arrays.asList`に置き換えても「必ず動く」という認識は改めた方がいいでしょう。

**といっても根本的にはその実装に問題があるのですが、、、**
著者は「`java.lang.ClassCastException: java.util.Arrays$ArrayList cannot be cast to java.util.ArrayList`」に遭遇してしまったので、今回の記事を書くことにしました。
インターフェース上は問題ないので、実行時までエラーになるとは判明しませんでした。

**今回のタイトルも正しくは「`java.util.Arrays.asList()`で`new ArrayList<>()`を置き換えると実装によってはエラーになる」です。**

(2018/11/24 追記)

#### どうしてもListを１行で定義、初期化したい場合の答え

```java:（方法１）Arrays.asList()をArrayListのコンストラクタに設定
List<String> fruts = new ArrayList<>(Arrays.asList("apple", "orange", "strawberry"));
```

`Arrays.asList()`を使えと指摘するなら、コンストラクタと合わせて使うのが適切です。

```java:（方法２）インスタンス生成時にメソッドを呼ぶ
List<String> fruts = new ArrayList<String>() {
    {
        add("apple");
        add("orange");
        add("strawberry");
    }
};
```
（方法２）は少し記述方法が独特ですが、文法上これも正しいです。