Arrays.asListメソッドが返すのはjava.util.ArrayListではない

# Arrays.asListとjava.util.ArrayListには互換性はない

可変長の引数からListオブジェクトを生成できるstaticメソッド、``Arrays.asList(T... t)``だが名前が似ているからって``java.util.ArrayList``のオブジェクトは返さない。

以下のようなコードはコンパイルエラーとなる。

```java
java.util.ArrayList<Integer> arrayList = Arrays.asList(1,2,3); // no instance(s) of type variable(s) T exist so that List<T> conforms to ArrayList<Integer>
```

List型で返されるので``java.util.ArrayList``にキャストすればよさげに見えるし、実際コンパイルエラーは消える。

```java
※やっちゃダメなキャスト
java.util.ArrayList<Integer> arrayList = (java.util.ArrayList<Integer>) Arrays.asList(1,2,3);
```

でも実行時に``ClassCastException``が出ちゃいます。絶対にやっちゃダメ。

# Arrays.asListの中身を見てみる

``Arrays.asList``メソッドが何を返しているかを確認するために実装を覗いてみる。

```Array.java
@SafeVarargs
    @SuppressWarnings("varargs")
    public static <T> List<T> asList(T... a) {
        return new ArrayList<>(a);
    }
...略
```

``return new ArrayList<>(a);``でArrayListを返しているじゃん！って言いたくなるけど、更にその``ArrayList``を辿ってみる。

```Array.java
/**
     * @serial include
     */
    private static class ArrayList<E> extends AbstractList<E>
        implements RandomAccess, java.io.Serializable
    {
        private static final long serialVersionUID = -2764017481108945198L;
        private final E[] a;

        ArrayList(E[] array) {
            a = Objects.requireNonNull(array);
        }
...略
```

なんとArrayクラス内にネストした``privateでstaticなArrayListクラス``を持っており、それをnewしてインスタンス生成していることがわかる。

そしてそのArrayListクラスは``AbstractList``を継承しており、``java.util.ArayList``クラスを継承してはいない。（java.util.ArayListもAbstractListを継承しているので継承の階層的には同じ）

__つまり、Arrays.asListは、java.util.ArrayListと同名（パッケージを除く）だが異なるクラス（継承関係も無い）のインスタンスを返していることが分かる。__

__``java.util.ArrayListクラス``と``Arraysクラス内のArrayListクラス``では互換性が無いのである。__

ゆえに上記の

```java
※やっちゃダメなキャスト
java.util.ArrayList<Integer> arrayList = (java.util.ArrayList<Integer>) Arrays.asList(1,2,3);
```
が```ClassCastException```を投げる理由が判明した。


# 公式ドキュメントを読んでみる
[link-Arrays]:https://docs.oracle.com/javase/jp/8/docs/api/java/util/Arrays.html
[Arraysクラスの公式ドキュメント][link-Arrays]を読んでみると、asListの説明には

>指定された配列に連動する固定サイズのリストを返します。

とある。

つまり、Arraysクラス内の__ArrayListクラスは固定長のリスト__であり、__可変長リストである``java.util.ArrayList``__との違いが分かる。

# Arrays.asListで取得したListにaddをしちゃダメ

上記で確認したようにArrays内のArrayListクラスは固定長なので、__リスト内の要素数を変更することはできない。__

なので以下のようなコードはコンパイルは通るが実行時に例外``UnsupportedOperationException``が発生してしまう。

```java
// 固定長リストに対して要素の追加をしようとしている
List<Integer> list1 = Arrays.asList(1,2,3);
list1.add(4);  // UnsupportedOperationException
```

[link-AbstractList]:https://docs.oracle.com/javase/jp/8/docs/api/java/util/AbstractList.html
これは、継承している[AbstractListクラスの公式ドキュメント][link-AbstractList]のaddメソッドの説明に

>この実装は、add(int, E)がオーバーライドされないかぎりUnsupportedOperationExceptionをスローします。

とあるように、Arrays内のArrayListクラスで``add(int, E)メソッド``をオーバーライドしていないためである。

Arrays.asListが固定長のリストを返すと分かっていないとaddをつかってしまう人も多いのではなかろうか。

## removeはどうか
ちなみにremoveメソッドも使うと実行時に``UnsupportedOperationException``が発生する。

あくまで固定長のリストなので、絶対にサイズを変えさせないという強い意志を感じる...。

# まとめ

``Arrays.asList``が返すリストと``java.util.ArrayList``には互換性はありません。キャストはしてはいけません。

ついでに``Arrays.asList``が返すリストは固定長のリストです。後からサイズを変えると例外が発生しちゃいます。

どうしても``java.util.ArrayList``型で使いたいなら、コンストラクタの引数に渡して使ってください。固定長じゃなくなるのでaddもremoveもできます。

```java

// コンパイルエラーも実行時にClassCastExceptionも発生しない
List<Integer> list1 = Arrays.asList(1,2,3);
ArrayList<Integer> list2 = new ArrayList<>(list1);
```

ただ、__Arrays.asListが返すのは固定長のリスト__というメリットが全く無くなってしまうので、わざわざこのようなコードを書く必要性は薄いと思いますし、リストはListインターフェース型で扱うのが一番良いと思います。