いつまでたっても間違いが無くなりません。
以下のように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); } }
分かりにくいですが、ここでインスタンス化される ArrayList
は Arrays
の内部クラスで以下のクラスです。
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; } // ... } }
内部データとして配列をそのまま持つラッパーです。
add
や remove
は 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$ArrayList
は Cloneable
ではないのです。。
まとめ
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 なリストが得られます。