Arrays.asList() は単なる配列のラッパを返すだけなので、要素の追加も削除もできません

いつまでたっても間違いが無くなりません。


以下のようにListの初期化で多用するArrays.asList()

List<String> stooges = Arrays.asList("Larry", "Moe", "Curly");

Arrays.asList() が返すインスタンスは、java.util.Arrays$ArrayList であって、java.util.ArrayList ではありません。


Arrays.asList() の実装は以下のようになっています。

public class Arrays {
    public static <T> List<T> asList(T... a) {
        return new ArrayList<>(a);
    }
}

分かりにくいですが、ここでインスタンス化される ArrayListArrays の内部クラスで以下のクラスです。

public class Arrays {

    private static class ArrayList<E> extends AbstractList<E>
        implements RandomAccess, java.io.Serializable {
        private final E[] a;

        ArrayList(E[] array) {
            if (array==null)
                throw new NullPointerException();
            a = array;
        }

        public int size() {
            return a.length;
        }

        public E get(int index) {
            return a[index];
        }

        public E set(int index, E element) {
            E oldValue = a[index];
            a[index] = element;
            return oldValue;
        }
        // ...
    }
}

内部データとして配列をそのまま持つラッパーです。

addremove は Override しないため、AbstractList の定義、すなわち

public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {

    public void add(int index, E element) {
        throw new UnsupportedOperationException();
    }

    public E remove(int index) {
        throw new UnsupportedOperationException();
    }
}

となり UnsupportedOperationException になります。

しかも、java.util.Arrays$ArrayListCloneable ではないのです。。

まとめ

Arrays.asList()set() で値の変更はできますが、要素の追加と削除はできない、配列とListのAPI互換を目的としたラッパーを生成します。

メソッドの戻り値にする場合はそのまま返さず、ArrayList などで返すのが良いです。

public List<String> stooges() {
    List<String> stooges = Arrays.asList("Larry", "Moe", "Curly");
    // ...
    return new ArrayList<>(stooges);
}

set() とかいらないので ImmutableList を返すような API が望ましかったのでしょうね。

Java9 からは、

List<String> stooges = List.of("Larry", "Moe", "Curly");

で immutable なリストが得られます。