今回は「java.util.Arrays.asList()
はnew ArrayList<>()
の代替ではない」という話について説明したいと思います。
いきなりですが例題です。
List<String> fruts = new ArrayList<>();
fruts.add("apple");
fruts.add("orange");
fruts.add("strawberry");
を
List<String> fruts = Arrays.asList("apple", "orange", "strawberry");
短くなるからと改修させるレビュアーは危険です。
void show(List<String> fruts);
コレクションとして操作しているなら問題ありませんが、インターフェースの実装によってはエラーとなる場合があるからです。
インスタンスの型を見れば分かりますがjava.util.Arrays.asList()
で作成されるインスタンスはjava.util.ArrayList
ではなくjava.util.Arrays$ArrayList
です。
問題が発生するとしたらこの違いに関するところになります。
以下のデモアプリで確認してみましょう。
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();
}
}
}
[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を1行で定義、初期化したい場合の答え
List<String> fruts = new ArrayList<>(Arrays.asList("apple", "orange", "strawberry"));
Arrays.asList()
を使えと指摘するなら、コンストラクタと合わせて使うのが適切です。
List<String> fruts = new ArrayList<String>() {
{
add("apple");
add("orange");
add("strawberry");
}
};
(方法2)は少し記述方法が独特ですが、文法上これも正しいです。