1. 程式人生 > >關於泛型萬用字元邊界的疑惑及解疑

關於泛型萬用字元邊界的疑惑及解疑

<? extends someclass>為什麼可以get而不能set?

void set(? extends someclass);

? extends someclass get(); 

  我們使用萬用字元通常是希望某一個例項可以向上轉型 例:

List<Animal> list = new ArrayList<Dog>() //compile error 

List<? extends Animal> list = new ArrayList<Dog>()  //compile ok

  這些是基本的概念,在這兒我就不再概述為什麼了,我們要講的是為什麼上界限定(? extends someclass)使用set方法

編譯錯誤,而使用get方法卻是安全的呢?

0)現在假想我們有一個Animal基類和若干子類(這些子類都繼承自Animal)

  讓我們先考慮一下陣列,假如你以如下的形式建立了一個數組.

Animal[] ani = new Dog[](10); //compile ok

  利用了向上轉型的多型思想,這是完全合法的 現在我們設想向裡面新增元素。

ani[0] = new Dog() //compile ok ,runtime ok

ani[1] = new Animal() //compile ok , runtime error

  我們知道現在操縱的是Dog型別的陣列,但是編譯器不知道(只有在執行時,ani引用才和dog陣列建立聯絡),編譯的時候編譯器沒

有理由會拒絕存放Animal型別的物件,因為他本身就是一個Animal型別的引用。

  陣列擁有特殊的保護機制,即使在編譯時期插入了錯誤的物件,執行時也能發現並丟擲這些異常,所以也沒必要為此擔心什麼。

1)帶萬用字元的set方法

現在假設你運用的是泛型,你能不能這樣做呢? 現在考慮如下例子

 List<? extends Animal> list = new ArrayList<Dog>() //compile ok

這也是向上轉型。

但有一點和陣列不同的是,陣列在編譯階段就確定了他是什麼型別的陣列引用(如上例是Animal型別的陣列引用),而帶泛型的List呢?他只知道他是一個某型別的List引用,只知道這個“某型別”是繼承自Animal的類,具體是Dog,Cat,還是Monkey呢,他一無所知。

你可能會提出疑問,後面不是跟了 new ArrayList<Dog>() 嗎,這不是在告訴他他是一個Dog型別的ArrayList引用嗎?很遺憾,編譯器沒有那麼聰明,這些只會在執行時建立聯絡,然而執行時的型別擦除又讓他們攜帶的資訊煙消雲散,你也就無法期待這些東西在執行時會給你什麼驚喜了。

那如果想向我們剛建好list裡面新增元素呢 如add(new Dog()) 或者add(new Animal())   (和set一樣,在此以add為例更好)

list.add(new Dog())    //compile error

list.add(new Animal()) //compile error

編譯器拒絕一切為其新增元素的操作(傳參操作),為什麼?因為 List<? extends Animal> list 無法確定他具體是什麼類,就像List<Animal>只能為其新增Animal及Animal的子類元素一樣,你一個連具體是什麼類都不知道的引用,編譯器自然認為任何新增都是不安全的。

讓我們再舉個容易理解的例子,我們暫時僅僅只觀察這個式子-------List<? extends Animal> 現在我們假設?是Cat,那麼我們能做些什麼?

我們能向裡面新增 Cat和Cat的子類元素,但是我們不能新增Dog,monkey甚至是Animal元素。

那現在返回來,編譯器無法得知 ? 具體是什麼型別,那 ?可能是Cat,也可能是Dog 或者其他的繼承自Animal的類,我們又怎麼能確定哪些add是安全的,哪些add是非法的呢?

所以我們自然無法使用add或者set方法。

2) get方法的合理性

我們知道get方法返回一個值,這個值只要是確定的編譯器就可以接受,現在考慮如下例子          List<Cat> test = new ArrayList<Cat>(); 
 test.add(new Cat());
 List<? extends Animal> list = test; 
 Animal ani = list.get(0); //compile ok,runtime ok get方法為什麼合理? 因為他有一個確定的返回值。 ? extends Animal 的返回值一定是一個Animal型別的 如果你對這句話有異議,那我建議你再回去溫習一下有關多型的知識,這確實是多型的一個體現,不管你?是代表Cat Dog 還是Monkey 這些類的共通點就是他們都是繼承自Animal的,這點編譯器心知肚明,自然也就不會有任何異議和不確定性,所以get方法確實是合理的

相關推薦

關於字元邊界疑惑解疑

<? extends someclass>為什麼可以get而不能set? void set(? extends someclass); ? extends someclass get();    我們使用萬用字元通常是希望某一個例項可以向上轉型 例: List&

學員優秀博文賞析:字元約束

       最課程師徒班的同學正在紅紅火火的學習中。彷彿、應該、必須,到了跟大家階段性彙報成果的時候了。為什麼,因為畢竟當時把師徒班說的那麼好,就業班的同學該要鬧革命了。        師徒班相較於就業班,雖然課程內容和總量完全一樣,但學員的學習過程掌握了更多的主動性。而檢驗主動性一個重要的標準,除了

java5-字元

如果有兩個類: Shape類 public class Shape{ //價錢 private String area; } Square類 public class Square extends Shape{ //價錢 pri

java基礎-/字元

泛型 泛型的意義: a:可以對型別進行自動檢查。 並不是替換,只是編譯期間進行檢查。 b:自動對型別進行轉換。 泛型到底是怎麼編譯的? 型別的擦除機制:》向上擦除》Object(最高到) 在編譯器編譯期間,把泛型全部擦除為object型別。 用jmap命

java遺珠之字元

我們在之前說過明確指定了泛型型別的引數之後,會把引數限制的很嚴格,萬用字元的作用就是放寬這種限制,有上限有界萬用字元<? extends A>,下限有界萬用字元<? extends B>,無界萬用字元<?>。上限有界和無界經常

Java——字元

萬用字元泛型在使用泛型特殊的場景下用到; 比如把泛型物件作為方法引數傳入方法的時候,就用到萬用字元泛型; package Month01.Day05.Demo02; import Month01.Day05.Demo01.Cat; import Month01.Day05.Demo01.D

javaSE——字元介面、型別擦出

一、萬用字元 1.萬用字元的概念 前面我們學習的泛型都已經解決了很多問題了,但是我們在使用的時候還是指定了型別的。比如泛型類,我們在建立這個類的物件的時候就指定了是什麼型別,然後就只能建立這個型別的物件,那麼我們有時候想要建立任意型別的物件的時候我們可以使用萬用字元。 1.1無解萬用字元"?

三句話總結JAVA字元(PECS)

在JAVA的泛型集合中,預設都可以新增null,除此以外,還有以下三條規則。 1. “?”不能新增元素 以“?”宣告的集合,不能往此集合中新增元素,所以它只能作為生產者(亦即它只能被迭代),如下: List<?> names = Lis

Java 字元?解惑

T  有型別 ?  未知型別 一、萬用字元的上界 既然知道List<Cat>並不是List<Anilmal>的子型別,那就需要去尋找替他解決的辦法, 是AnimalTrianer.act()方法變得更為通用(既可以接受List<Anima

Java 字元解釋

前言 最近看Rxjava retrofit 中到處都是泛型型別定義,不得不重新整理一下資料,簡單介紹,滿足基本理解使用。 定義什麼是泛型? 泛型,即“引數化型別”。一提到引數,最熟悉的就是定義方法時有形參,然後呼叫此方法時傳遞實參。那麼引數化型別怎麼理

--字元的上下限

##泛型 ####泛型概述   泛型:可以在類或者方法當中預支的使用未知的資料型別。 備註:一般在建立物件的時候,將未知的資料型別確定為具體的資料型別,當沒有指定泛型時,預設型別為Object型別。 ####使用泛型的好處   避免了型別轉換的麻煩,儲存的是什麼樣的資料型別,取出的就是什麼樣的資料型

jQuery選擇器中的字元[id*='id']jquery選擇器總結

1.選擇器 (1)萬用字元: $("input[id^='code']");//id屬性以code開始的所有input標籤 $("input[id$='code']");//id屬性以code結束的所有input標籤 $("input[id

java基礎總結 --- 擦除、邊界字元

* 擦除的問題 * 為什麼要擦除: 1.5版本才出現泛型 為了相容之前地程式碼 * 它使得泛化的客戶端可以用非泛化的類庫來使用。 * 以及不破壞現有類庫的情況下,將泛型融入java語言。 * 擦除使得現有的非泛型客戶端程式碼能夠在不改變的情況繼續使用,直至客戶端準

Java筆記 – 方法 介面 擦除 邊界 字元

Java中的泛型參考了C++的模板,Java的界限是Java泛型的侷限。 2、簡單泛型 促成泛型出現最引人注目的一個原因就是為了創造容器類。 首先看一個只能持有單個物件的類,這個類可以明確指定其持有的物件的型別 class Holder1 { private Circle a; p

java中的字元?問題

本文是經過網上查詢的資料整合而來,給自己做個筆記,也分享給大家!需要你對泛型有一定的基礎瞭解。 package Test8_8; import java.util.ArrayList; import java.util.List; class Animal { privat

29---限定(上限+下限+上限的體現+下限的體現+字元的體現)+集合查閱的技巧

一、泛型上限 1、迭代並列印集合中的元素 (1)集合即可能是List,也可能是Set,用Collection提高擴充套件性 (2)當容器中存放的元素不確定,且裡面不準備使用具體型別的情況下,使用萬用字元 注: (1)萬用字元:?,未知型別。不明確型別時,可以用?來表示,意味著什麼

Java方法和型別字元的區別

泛型方法VS型別萬用字元(兩者可以混用):      1)你會發現所有能用型別萬用字元(?)解決的問題都能用泛型方法解決,並且泛型方法可以解決的更好: 最典型的一個例子就是:          

day26基礎加強(字元、註解、反射 註解使用)

泛型 1、具有一個或多個泛型變數的型別被稱之為泛型類 class A<T>{} 2、在建立泛型類例項時,需要為其型別變數賦值 A<String> a = new A<String>(); 如果建立例項時,不給型別變數賦值,那麼會有一個警告。 3

Java 字元

Java 泛型和萬用字元        很多時候,反射並不能滿足業務的需求,Java 泛型和萬用字元與反射配合使用可使得反射的作用發揮到完美的地步,從而實現真正的通用。 直接上程式碼 import java.

Java基礎系列(三十七):繼承,字元反射

泛型型別的繼承規則 首先,我們來看一個類和它的子類,比如 Fruit 和 Apple。但是Pair<Apple>是Pair<Fruit>的一個子類麼?並不是。比如下面的這段程式碼就會編譯失敗: Apple[] apples = ...; Pair<F