Java容器——UnsupportedOperationException

    先看一個例子:java

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

public class UnsupportedOperation {
    static void test(String msg, List<String> list) {
        System.out.println("----------" + msg + "----------");
        Collection<String> c = list;
        Collection<String> subList = list.subList(1, 4);
        Collection<String> subList2 = new ArrayList<>(subList);
        try { c.retainAll(subList2); } catch (Exception e) {
            System.out.println("retainAll: " + e);
        }
        try { c.removeAll(subList2); } catch (Exception e) {
            System.out.println("removeAll: " + e);
        }
        try { c.clear(); } catch (Exception e) {
            System.out.println("clear: " + e);
        }
        try { c.add("X"); } catch (Exception e) {
            System.out.println("add: " + e);
        }
        try { c.addAll(subList2); } catch (Exception e) {
            System.out.println("addAll: " + e);
        }
        try { c.remove("C"); } catch (Exception e) {
            System.out.println("remove: " + e);
        }
        try { list.set(0, "W"); } catch (Exception e) {
            System.err.println("List.set: " + e);
        }
        System.out.println("List.className=" + list.getClass().getSimpleName());
    }
    public static void main(String[] args) {
        List<String> list = Arrays.asList("A B C D E F G".split(" "));
        test("Modifiable Copy", new ArrayList<String>(list));
        test("Arrays.asList", list);
        test("Unmodifiable List", Collections.unmodifiableList(list));
    }
}

執行結果:編程

----------Modifiable Copy----------
List.className=ArrayList
----------Arrays.asList----------
retainAll: java.lang.UnsupportedOperationException
removeAll: java.lang.UnsupportedOperationException
clear: java.lang.UnsupportedOperationException
add: java.lang.UnsupportedOperationException
addAll: java.lang.UnsupportedOperationException
remove: java.lang.UnsupportedOperationException
List.className=ArrayList
----------Unmodifiable List----------
retainAll: java.lang.UnsupportedOperationException
removeAll: java.lang.UnsupportedOperationException
clear: java.lang.UnsupportedOperationException
add: java.lang.UnsupportedOperationException
addAll: java.lang.UnsupportedOperationException
remove: java.lang.UnsupportedOperationException
List.set: java.lang.UnsupportedOperationException
List.className=UnmodifiableRandomAccessList

    那麼爲何會出現這樣的結果呢?對於Unmodifiable的List不能作修改很好理解,但從結果中能夠看到,一樣是ArrayList,爲何Array.asList()返回的List就不能作修改呢?數組

    Arrays.asList()會生成一個List,它基於一個固定大小的數組,僅支持那些不會改變數組大小的操做,任何對引發底層數據結構的尺寸進行修改的方法都會產生一個UnsupportedOperationException異常,以表示對未獲支持操做的調用。數據結構

    其實上面的代碼中,我故意使用的是getSimpleName(),由於這個方法不會顯示包名。若是咱們將dom

System.out.println("List.className=" + list.getClass().getSimpleName());

    替換爲:設計

System.out.println("List.className=" + list.getClass().getName());

    那麼結果就是這樣的了:代理

----------Modifiable Copy----------
List.className=java.util.ArrayList
----------Arrays.asList----------
retainAll: java.lang.UnsupportedOperationException
removeAll: java.lang.UnsupportedOperationException
clear: java.lang.UnsupportedOperationException
add: java.lang.UnsupportedOperationException
addAll: java.lang.UnsupportedOperationException
remove: java.lang.UnsupportedOperationException
List.className=java.util.Arrays$ArrayList
----------Unmodifiable List----------
retainAll: java.lang.UnsupportedOperationException
removeAll: java.lang.UnsupportedOperationException
clear: java.lang.UnsupportedOperationException
add: java.lang.UnsupportedOperationException
addAll: java.lang.UnsupportedOperationException
remove: java.lang.UnsupportedOperationException
List.className=java.util.Collections$UnmodifiableRandomAccessList
List.set: java.lang.UnsupportedOperationException

    能夠看到,Arrays.asList返回的名字雖然叫ArrayList,但此ArrayList是Arrays中的一個內部類,而非java.util.ArrayList。code

查看其源碼,能夠發現此Arrays.ArrayList內部類的聲明以下:對象

private static class ArrayList<E> extends AbstractList<E>
        implements RandomAccess, java.io.Serializable

    而該類的實現方法中,並無實現修改E[]數組(增、刪、清除等)的方法接口

    所以對這種對象調用這些方法時會調用其父類AbstractList的實現,因而就直接拋出了UnsupportedOperationException:

    所以,一般的作法是,把Arrays.asList()的結果做爲構造方法的參數傳遞給任何Collection(或者使用addAll()方法或Collections.addAll()靜態方法),這樣就能夠生成容許使用全部的方法的普通容器——這在main()中的第一個對test()的調用中獲得了展現,這樣的調用會產生新的尺寸可調的底層數據結構。Collections類的「不可修改」方法將容器包裝到了一個代理中,只要你執行任何試圖修改容器的操做,這個代理就會產生UnsupportedOperationException異常。使用這些方法的目標就是產生「常量」容器對象。

    test()中的最後一個try語句塊將檢查做爲List的一部分set()方法。Arrays.asList()返回固定大小的List,而Collections.unmodifiableList()產生不可修改的列表。正如輸出中看到的,修改Arrays.asList()返回的List中的元素是能夠的,由於這沒有違反「大小固定」這一特性。但很明顯,unmodifiableList()的結果在任何狀況下都是不可修改的。

--------------------------------------------------------------

    像上述拋出異常的操做,在Java容器中被稱爲「未獲支持的操做」。那麼爲何會將方法定義爲可選的呢?那是由於這樣作能夠防止在設計中出現接口爆炸的狀況。爲了讓這種方式能工做:

    1. UnsupportedOperationException必須是一種罕見的事件。即,對於大多數類來講,全部操做都應該能夠工做,只有在特例中才會有這類操做。在Java容器中也確實如此,由於你在99%的時間裏使用的容器類,如ArrayList、LinkedList、HashSet和HashMap,以及其餘的具體實現,都支持全部的操做。

    2. 若是一個操做是未獲支持的,那麼在實現接口的時候可能就會致使UnsupportedOperationException異常,而不是將產品交付給客戶之後纔出現此異常,這種狀況是有道理的,畢竟,它表示編程上有錯誤:使用了不正確的接口實現。

    值得注意的是,這類操做只有在運行時才能探測到。

相關文章
相關標籤/搜索