※ 引述《LaPass (LaPass)》之銘言:
: //下面這段OK
: List<A> lista;
: List<C> listc=new ArrayList<>();
: listc.add(new C());
: //lista = listc; // X
: lista=(List)listc; //OK
: A a=lista.get(0); //OK
: System.out.print(a.getStr()); //印出 C
你可以瞭解一下「共變性」…
Java的泛型並不具有共變性,不過可以使用型態通配字元?與extends來宣告變數,使其達
到類似共變性。
http://openhome.cc/Gossip/Java/Generics-extends.html
如果需要達到類似共變性的功能,可以用 ? extends,這樣就不用 CAST 來關掉編譯器
型態檢查功能 …
List<? extends A> lista;
List<C> listc = new ArrayList<>();
listc.add(new C());
lista = listc; // OK,類似共變性
A a=lista.get(0); //OK
System.out.print(a.getStr()); //印出 C
: //讓我們反過來試試看,這樣也是ok的
: List<A> lista=new ArrayList<>();
: List<C> listc;
: lista.add(new C()); //OK 自動轉型
: //listc = lista; // X
: listc=(List)lista; //OK
: A a=listc.get(0); //OK
: System.out.print(a.getStr()); //印出C
可以研究一下「逆變性」,如果Node<A>視為一種Node<B>,則稱Node具有
逆變性(Contravariance)。Java泛型並不支援逆變性,可以使用型態通配字元?
與super來宣告,以達到類似逆變性的效果
http://openhome.cc/Gossip/Java/Generics-super.html
如果需要達到類似逆變性的功能,可以用 ? super,這樣就不用 CAST 來關掉編譯器
型態檢查功能 …
List<A> lista=new ArrayList<>();
List<? super C> listc;
Lista.add(new C()); //OK 這不是自動轉型,這是符合 is-a 關係
listc = lista; // OK,類似逆變性
A a= (A) listc.get(0); // 確定沒問題,關掉編譯器型態檢查
System.out.print(a.getStr()); //印出C
: //編譯OK但是執行不OK
: List<A> lista=new ArrayList<>();
: List<C> listc;
: lista.add(new A()); //OK 自動轉型
: //listc = lista; // X
: listc=(List)lista; //OK
: C c=listc.get(0); //編譯OK,執行錯誤
: ClassCastException: A cannot be cast to C
因為 CAST 實際上是關掉編譯器型態檢查的功能,實際上 listc 參考的實例,
當中是維護了一串 A 實例,你又編譯時期叫編譯器住嘴,當然執行時期出錯就得
自行收拾了 … XD
: //再來看一個更驚悚的
: List<A> lista=new ArrayList<>();
: List<B> listb;
: listb=(List)lista; //OK
: listb.add(new B()); //OK.... 慢著,這很明顯有問題啊!
: B b=listb.get(0); //可是還是被我拿出來了
: A a=lista.get(0); //編譯ok,執行時才出錯
: ClassCastException: B cannot be cast to A
這不是驚不驚悚的問題,CAST 是關掉編譯器型態檢查功能,在不確定內容物究竟為何
,就進行 cast(或者是故意 cast),想不出錯很難吧! … XD
簡單來說,只有在確實清楚實例的型態為何下,再進行 CAST(也就是要編譯器別囉嗦)
…XD
: 因為java在運行時根本不會記住那個物件的泛型型別
是的,泛型提供的資訊只是給編譯器看的 ...