中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

抖音二面栽在泛型擦除?答應我,別和我倒在同一地方!

發布時間:2020-08-09 03:20:24 來源:ITPUB博客 閱讀:164 作者:yilian 欄目:移動開發

前言

這是我在抖音二面的時候自我感覺沒有答好的一題。因為我的中心只是圍繞在了 TObject替換的問題上了,并沒有去講解他會帶來的問題。

思維導圖

抖音二面栽在泛型擦除?答應我,別和我倒在同一地方!

什么是泛型擦除?

其實我們很常見這個問題,你甚至經常用,只是沒有去注意罷了,但是很不碰巧這樣的問題就容易被面試官抓住。下面先來看一段代碼吧。

  List list = new ArrayList();  List listString = new ArrayList<String>();  List listInteger = new ArrayList<Integer>();

這幾段代碼簡單、粗暴、又帶有很濃厚的熟悉感是吧。那我接下來要把一個 數字1插入到這三段不一樣的代碼中了。

作為讀者的你可能現在已經黑人問號了????你肯定有很多疑問,這明顯不一樣啊,怎么可能。

  public class Main {      public static void main(String[] args) {          List list = new ArrayList();          List listString = new ArrayList<String>();          List listInteger = new ArrayList<Integer>();          try {              list.getClass().getMethod("add", Object.class).invoke(list, 1);
              listString.getClass().getMethod("add", Object.class).invoke(listString, 1);              // 給不服氣的讀者們的測試之處,你可以改成字符串來嘗試。
              listInteger.getClass().getMethod("add", Object.class).invoke(listInteger, 1);
          } catch (Exception e) {
              e.printStackTrace();
          }
          System.out.println("list size:" + list.size());
          System.out.println("listString size:" + listString.size());
          System.out.println("listInteger size:" + listInteger.size());
      }
  }
抖音二面栽在泛型擦除?答應我,別和我倒在同一地方!

不好意思,有圖有真相,我就是插進去了,要是你還不信,我還真沒辦法了。

探索真相

上述的就是泛型擦除的一種表現了,但是為了更好的理解,當然要更深入了是吧。雖然 List很大,但卻也不是不能看看。

兩個關鍵點,來驗證一下:

  1. 數據存儲類型
  2. 數據獲取
  // 先來看看畫了一個大餅的List
  // 能夠過很清楚的看到泛型E
  public class ArrayList<E> extends AbstractList<E>          implements List<E>, RandomAccess, Cloneable, java.io.Serializable{       
      // 第一個關鍵點    
      // 還沒開始就出問題的存儲類型
      // 難道不應該也是一個泛型E?
      transient Object[] elementData;      public E get(int index) {
          rangeCheck(index);          return elementData(index); // 1---->
      }      // 由1直接調用的函數
      // 第二個關鍵點,強制轉化得來的數據
      E elementData(int index) {          return (E) elementData[index];
      }
  }

我想,其實你也能夠懂了,這個所謂的泛型 T最后會被轉化為一個 Object,最后又通過強制轉化來進行一個轉變。從這里我們也就能夠知道為什么我們的數據從前面過來的時候, String類型數據能夠直接被 Integer進行接收了。

帶來什么樣的問題?

(1) 強制類型轉化

這個問題的結果我們已經在上述文章中提及到了,通過反射的方式去進行插入的時候,我們的數據就會發生錯誤。

如果我們在一個 List<Integer>中在不知情的情況下插入了一個 String類型的數值,那這種重大錯誤,我們該找誰去說呢。

(2)引用傳遞問題

上面的問題中,我們已經說過了 T將在后期被轉義成 Object,那我們對引用也進行一個轉化,是否行得通呢?

  List<String> listObject = new ArrayList<Object>();
  List<Object> listObject = new ArrayList<String>();

如果你這樣寫,在我們的檢查階段,會報錯。但是從邏輯意義上來說,其實你真的有錯嗎?

假設說我們的第一種方案是正確的,那么其實就是將一堆 Object數據存入,然后再由上面所說的強制轉化一般,轉化成 String類型,聽起來完全ok,因為在 List中本來存儲數據的方式就是 Object。但其實是會出現 ClassCastException的問題,因為 Object是萬物的基類,但是強轉是為子類向父類準備的措施。

再來假設說我們的第二種方案是正確的,這個時候,根據上方的數據 String存入,但是有什么意義存在呢?最后都還是要成 Object的,你還不如就直接是 Object

解決方案

其實很簡單,如果看過一些公開課想來就見過這樣的用法。

  public class Part<T extends Parent> {      private T val;      public T getVal() {          return val;
      }      public void setVal(T val) {          this.val = val;
      }
  }

相比較于之前的 Part而言,他多了 <T extends Parent>的語句,其實這就是將基類重新規劃的操作,就算被編譯,虛擬機也會知道將數據轉化為 Parent而不是直接用 Object來直接進行替代。

應用場景

這里需要感謝給我提出問題的大佬讀者:挖掘機技術

該部分的思路來自于 Java泛型中extends和super的區別?

上面我們說過了解決方案,使用 <T extends Parent>。其實這只是一種方案,在不同的場景下,我們需要加入不同的使用方法。另外官方也是提倡使用這樣的方法的,但是我們為了避免我們上述的錯誤,自然需要給出一些使用場景了。

基于的其實是兩種場景,一個是擴展型 super,一個是繼承型 extends。下面都用一個列表來舉例子。

統一繼承順序

  // 承載者
  class Plate<T>{      private T item;      public Plate(T t){item=t;}      public void set(T t){item=t;}      public T get(){return item;}
  }  // Lev 1
  class Food{}  // Lev 2
  class Fruit extends Food{}  class Meat extends Food{}  //Lev 3
  class Apple extends Fruit{}  class Banana extends Fruit{}  class Pork extends Meat{}  class Beef extends Meat{}  //Lev 4
  class RedApple extends Apple{}  class GreenApple extends Apple{}

<T extends Parent>

繼承型的用處是什么呢?

其實他期待的就是這整個列表的數據的基礎都是來自我們的 Parent,這樣獲取的數據全部人的父類其實都是來自于我們的 Parent了,你可以叫這個列表為 Parent家族。所以也可以說這是一個適合頻繁讀取的方案。

抖音二面栽在泛型擦除?答應我,別和我倒在同一地方!
抖音二面栽在泛型擦除?答應我,別和我倒在同一地方!
  Plate<? extends Fruit> p1=new Plate<Apple>(new Apple());
  Plate<? extends Fruit> p2=new Plate<Apple>(new Beef()); // 檢查不通過
  // 修改數據不通過
  p1.set(new Banana());  // 數據獲取一切正常
  // 但是他只能精確到由我們定義的Fruit
  Fruit result = p1.get();

<T super Parent>

擴展型的作用是什么呢?

你可以把它當成一種兼容工具,由 super修飾,說明兼容這個類,通過這樣的方式比較適用于去存放上面所說的 Parent列表中的數據。這是一個適合頻繁插入的方案。

抖音二面栽在泛型擦除?答應我,別和我倒在同一地方!
抖音二面栽在泛型擦除?答應我,別和我倒在同一地方!
  // 填寫Food的位置,級別一定要大于或等于Fruit
  Plate<? super Fruit> p1=new Plate<Food>(new Apple());  // 和extends 不同可以進行存儲
  p1.set(new Banana());  // get方法
  Banana result1 = p1.get(); // 會報錯,一定要經過強制轉化,因為返回的只是一個Object
  Object result2 = p1.get(); // 返回一個Object數據我們已經屬于快要丟失掉全部數據了,所以不適合讀取

以上就是我的學習成果,如果有什么我沒有思考到的地方或是文章內存在錯誤,歡迎與我分享。

最后有話說

附上我的Android核心技術學習大綱,獲取相關內容來我的GitHub一起玩耍: https://github.com/Meng997998/AndroidJX
vx:xx1341452

對于進階這條路而言,學習是會有回報的!

你把你的時間投資在學習上,就意味著你可以收獲技能,更有機會增加收入。

在這里分享我的Android學習PDF大全來學習,這份Android學習PDF大全真的包含了方方面面了,內含Java基礎知識點、Android基礎、Android進階延伸、算法合集等等

抖音二面栽在泛型擦除?答應我,別和我倒在同一地方!

我的這份學習合集,可以有效的幫助大家掌握知識點。

總之也是在這里幫助大家學習提升進階,也節省大家在網上搜索資料的時間來學習,也可以分享給身邊好友一起學習

獲取方式:關注我看個人介紹,或直接  點擊我免費領取

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

高阳县| 鄂托克前旗| 杭锦后旗| 大新县| 皋兰县| 农安县| 江都市| 黄浦区| 高邑县| 乐至县| 蛟河市| 雅江县| 永定县| 德江县| 凤台县| 九寨沟县| 崇文区| 靖西县| 林口县| 红河县| 阿拉善右旗| 乌兰县| 新民市| 芜湖县| 铜山县| 临安市| 南宫市| 大兴区| 尼勒克县| 四平市| 和硕县| 织金县| 萍乡市| 吴桥县| 都兰县| 全南县| 探索| 尉氏县| 吉木萨尔县| 沙坪坝区| 芦山县|