LoginSignup
6
9

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-11-24

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

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

after
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行で定義、初期化したい場合の答え

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

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

(方法2)インスタンス生成時にメソッドを呼ぶ
List<String> fruts = new ArrayList<String>() {
    {
        add("apple");
        add("orange");
        add("strawberry");
    }
};

(方法2)は少し記述方法が独特ですが、文法上これも正しいです。

6
9
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
9