解決許多java開發 或者android開發 在平時寫一些基礎架構,或者是造一些輪子的時候不敢用泛型,用很差泛型的問題。 甚至有些人使用泛型的時候報錯都只會用idea提示的方法來修改代碼,殊不知這樣改的緣由,也不知道強轉泛型會有什麼惡果。java
先定義一個模仿List 的泛型list。 咱們來看看這個乞丐版的list能幫咱們作什麼事android
public class CustomList<T> { Object[] array = new Object[0]; public T get(int index) { return (T) array[index]; } public void add(T instance) { array[array.length - 1] = instance; } } 複製代碼
看看怎麼使用他bash
CustomList<String> customList = new CustomList<>(); customList.add("hahahaha"); String c = customList.get(0); 複製代碼
到這,咱們來看看 到底有啥好處。 首先看這個add方法,有了泛型之後,咱們就不須要擔憂類型轉換錯誤了。 由於咱們在定義的時候 指定了泛型的類型,因此若是咱們在調用add方法的時候傳了一個 非string類型的 那麼ide就會報錯了,即便你不用ide 用記事本寫,你編譯起來也會報錯的。 這就是靜態語言的好處了, 不少bug 在編譯的時候告訴你 不用像js 那麼蛋疼。markdown
而後再看看get 這個函數,想一下 若是沒有泛型的話, 咱們get出來的值 是必定要強轉成string才能賦值給c的, 可是如今有了泛型, 因此你能夠直接get出來,這個類型轉換的東西 早就幫你作好了。架構
總結一下泛型的好處:app
這邊能夠想一下,爲何泛型不能用靜態的去定義?你怎麼改都是沒法突破這個規則,是沒法編譯成功的。ide
前面的例子咱們能夠知道,泛型主要用來能夠初始化每一個實例的。 注意是每一個實例,他是動態肯定的,函數
取決於你當時使用的時候 傳的是什麼參數,好比List<Object> List<String> List<Teacher>ui
對於一個靜態變量來講 你若是用泛型 那就會亂套了。 例如咱們上面截圖的例子你用泛型會發生什麼?idea
static Object ins? static String ins? static Teacher ins? 你們都叫ins,那我怎麼知道 這個ins
到底應該是哪一個類型的? 靜態變量 全局惟一啊。 因此泛型是絕對不能用static來修飾的。
這個地方必定要想明白了,想明白了,對你理解泛型是有好處的。
這種就是一種典型的錯誤寫法,明明接口中有泛型的,結果實體類中 把這個泛型給抹掉了。
雖然能夠編譯經過,可是這種寫法就毫無心義了。
這種纔是正確的寫法,和前面那種錯誤的寫法相比 咱們明顯能夠省略一次強制類型轉換。
你們能夠比對一下這2種寫法 和 文章開頭泛型的2個優勢。仔細體會一下。
interface IShop<T> { T buy(float price); } interface IPhoneShop2<T> extends IShop<T> { void repair(T phone); } 複製代碼
前面咱們說道 ,泛型最大的好處就是方便使用,好比上面的代碼 咱們使用起來就很輕鬆如意,可是由於這樣的寫法 太隨意 因此要加一層限制。 上面的代碼中,咱們明明是一個手機商店,但實際使用的時候 卻能夠隨便傳, 傳String 傳Apple 傳啥都行。 這和設計者的本意是不一致的。
因此泛型還能夠加限制
interface Phone {
}
interface IPhoneShop2<T extends Phone> extends IShop<T> {
void repair(T phone);
}
複製代碼
這樣一來就能夠限制咱們使用時的類型,限制他必定得是Phone的類型才行。 考慮到java 是支持多接口,可是不支持多繼承的,泛型的限制也遵循這個規定。
來說講泛型中一個令不少人想不通的地方
定義一個水果 而後有蘋果和香蕉
interface TFruit {
}
class Apple2 implements TFruit {
}
class Banana2 implements TFruit {
}
複製代碼
而後咱們看看他們的使用
報紅的地方爲何報錯 是不少人想不明白的地方嗎,咱們的apple 命名是fruit的子類啊,爲啥報錯?
換個角度來思考一下這個問題:
因此對於 list 的 泛型來講, 他的類型 是動態肯定的, 是沒辦法在編譯期 肯定的, 因此你須要保證他 在= 兩邊 泛型都是絕對一致的 才能聲明成功,不然一定失敗
繼續看:
這個例子其實不難理解 爲何add 方法不能編譯經過。
看到這,我相信不少人 都想告辭了。。。這tmd 泛型限制真多,咋用?不用了,之後本身造輪子自動屏蔽泛型了。
不要緊 耐心一下,咱們再縷一遍。
// =左邊: 表明 我想要一個水果 =右邊 :我給你個蘋果 邏輯沒問題 編譯經過
TFruit fruit = new Apple2();
// =左邊: 表明 我想要一個水果的list 任意的水果 =右邊 :我給你個任意水果的list 邏輯沒問題 編譯經過
List<TFruit> tf1 = new ArrayList<TFruit>();
//既然是個水果的list 那我 add 蘋果香蕉 確定沒問題
tf1.add(new Apple2());
tf1.add(new Banana2());
// =左邊: 表明 我想要一個水果的list 任意的水果 =右邊 :我給你一個蘋果的list
//這樣編譯確定不經過,由於我想要的是水果的list 你卻給我一個蘋果的list 這樣你讓我就沒辦法玩了
// 我想要水果 你只給我蘋果 那香蕉 葡萄 西瓜 我就沒辦法要了,因此你確定不行 編譯不過
List<TFruit> tf2 = new ArrayList<Apple2>();
//=左邊: 表明 我想要一個list,這個list 必須是一個水果的類型,且只能是一種水果的類型 =右邊 :我給你一個蘋果的list
//符合要求 編譯經過
List<? extends TFruit> tf3 = new ArrayList<Apple2>();
//我這個tf3 要求的是必須是一種水果的類型,可是我並不知道是那種類型,多是水果 多是葡萄 多是香蕉
// 因此你直接往我這塞一個肯定好的水果 我確定是不接受的,編譯確定失敗
tf3.add(new Apple2());
tf3.add(new Banana2());
複製代碼
前面的文章看完,是否是以爲 這個?extends 有點蠢了,實際上 他在某種場景下 是 十分有用的(廢話 否則java爲啥要這麼設計)
仍是上面的水果,咱們增長一個方法 返回對應水果的價格
interface TFruit { int getPrice(); } class Apple2 implements TFruit { @Override public int getPrice() { return 1; } } class Banana2 implements TFruit { @Override public int getPrice() { return 2; } } 複製代碼
看看會有什麼問題
看這個函數的參數, 這個函數的參數 意思是 我想要一個list ,這個list裏面 能夠聽任何水果, 只要是水果就行,
可是在調用的時候 咱們給他的 倒是蘋果的list 和 香蕉的list ,這就不是他想要的了,我想要任意類型的水果 你卻給我 蘋果或者是香蕉的,你幫我指定了具體類型 那確定是不能夠的。
因此這個時候 ? extends 就出場了
改完之後 就直接編譯成功了:
回想一下上一小節的內容,這個? extends 不就是表明 想要任意一種水果嗎
你既然想要的是任意 一種水果,那我給你蘋果或者香蕉 確定是ok的。
並且是運行期間報錯了。後果比較嚴重,不易察覺。
一個香蕉固然不能轉成蘋果。
平時寫代碼的時候必定不要這麼寫。
改爲
就能夠, 這裏怎麼理解?
加上? super就表明 等號右邊的東西 你只要能夠接受一個蘋果的list就能夠了。