15個問題告訴你如何使用Java泛型

華為雲開發者社群發表於2021-04-28
摘要:Java泛型其本質是引數化型別,也就是說所操作的資料型別被指定為一個引數(type parameter)這種引數型別可以用在類、介面和方法的建立中,分別稱為泛型類、泛型介面、泛型方法。

Java泛型是J2 SE1.5中引入的一個新特性,其本質是引數化型別,也就是說所操作的資料型別被指定為一個引數(type parameter)這種引數型別可以用在類、介面和方法的建立中,分別稱為泛型類、泛型介面、泛型方法。

泛型方法

一般定義如下,即方法的前面加了個<T>

public class FTest {
    public <T> List<T> f(T t){...};
}

三種泛型引數推斷方式:

1、直接在f()前面加確定泛型

fTest.<Integer>f(xxx)

2、通過輸入引數確定, 下面這個推斷為Integer

int number = 0;
fTest.f(number)

3、可通過 返回值 確定

List<Integer> list = fTest.f(xxx);

Q: 下面這段程式碼哪裡有問題? 是toString()那裡嗎?

public class A<T> {
    public static void  test(T t){
          System.out.println(t.toString());  
  }
}

A:test是static方法, 因此無法感知A<T>例項裡的T
需要改成
public static <T> void test(T t)

toString()那裡沒問題,toString就是Object的方法。

泛型引數和型別消除

Q: 泛型引數T在執行時,會變成什麼?
A: 統一變成Object且不包含任何型別資訊。

Q: 泛型引數T可以可以使用instanceof做比較嗎?

class A<T> {
   void f(Object arg)
   if(arg instanceof T) {
      ...
   }
}

A: 不能,編譯器會報錯。

Q: 泛型引數T可以進行new T()或者new T[]操作嗎?
A: 不能,編譯器會報錯。

15個問題告訴你如何使用Java泛型

Q: 能呼叫泛型引數物件裡的方法嗎?

T.f();

A: 只能呼叫Object的方法。

Q: 可以用T做強制轉化嗎?

T t = (T)object;

A: 能執行, 但不會真正發生轉型, 編譯時會觸發waring警告。

新建泛型物件時的問題

先假定有2個類, 基類Parent 和子類Child

class Parent{}
class Child extends Parent{}

回答以下問題:
Q:下面這句話有問題嗎?

List<Parent> list = new ArrayList<Child>()

A:有問題,編譯就錯誤了。 List<Parent>和ArrayList<Child>並不存在父子類的關係

Q:

List<? extends Parent> list = new ArrayList<Child>();

這個list有什麼特點?

A:這個list可以呼叫A a = list.get(), 但是不能list.add(new Parent())

  • 原因:
    list.get()所做的操作是在返回時, 把內部的<? extend Parent> 強轉成Parent, 是合理的,任何Parent的子類都可以轉成Parent
    list.add(new Parent())所做的操作是在輸入時, 把外部的A轉成內部的<? extend Parent>, 這是不合理的,因為我們不知道這個Parent物件可以轉成哪個Parent的子類。

Q:

List<? super Child> list = new ArrayList<Parent>();

這個list有什麼特點?
下面誰會報錯

list.add(new Child())
list.add(new Parent())
Parent a= list.get();
Child b = list.get()

A:截圖如下:

15個問題告訴你如何使用Java泛型

  • Child c = list.get() 或者Parent p = list.get()所做的操作是在返回時, 把內部的<? super Child> 強轉成外部的Parent或者child, 是不合理的, 因為編譯器覺得child的父類 不一定 能轉成parent或者child,所以禁止了這種行為( 比如parent的父類是object, 但object不一定就能轉成parent或者child)。*list.add(new Child())所做的操作是在輸入時, 把外部的child或者parent轉成內部的<? super Child>, 這是合理的,因為child和parent一定能轉成child的父類。

Q:

List<?> list = new ArrayList<A>();

這個list有什麼特點?

A:get和add都不行,只能做remove等無返回值無輸入A的操作。
PS: 注意,不是說不能呼叫get或add方法, 而是呼叫get或add時,不能使用A這個物件去操作。
即無法做add(A) 或者 A a = get(0)
但是可以做add(object) 或者Object o = get(0)
因為?可以轉為Object, 但是無法轉為A。

Q:下面這個程式碼會報錯嗎?

   List<Fruit> fruitList = new ArrayList<>();
   fruitList.add(new Fruit());
   List<Apple> appleList = new ArrayList<>();
   appleList.add(new Apple());
   fruitList.addAll(appleList);
   System.out.println(fruitList);

A:不會報錯。會正常列印結果。

15個問題告訴你如何使用Java泛型

PECS原則
注意PECS原則和上面的區別!
上面之前提到的? extend或者? supert, 都是在宣告物件的時候用的。
而PECS原則是用於泛型物件的方法輸入引數!

假設有一個類定義如下:

public static class MyList<T> {
    List<T> list = new ArrayList<>();

    // 把輸入引數塞給自己,類似於生產操作
    public void pushList(List<T> t) {
        list.addAll(t);
    }

    // 把自己的內容塞給輸入引數,類似於讓輸入引數做消費。
    public void pollList(List<T> t) {
         t.addAll(list);
    }
}

則T就是泛型引數。

Q:下面程式碼能正常執行嗎?

MyList<Number> myList = new MyList<>();

List<Integer> intList = new ArrayList<>();
myList.pushList(intList);

List<Object> objectList = new ArrayList<>();
myList.pollList(objectList);

A:不能正常執行, pushList和pollList都會報錯

因為編譯器檢查後,認為 List<Integer>和List<Number>不是一個東西!

Q: 如果上文要支援pushList,應該怎麼修改pushList方法的定義?
A:改成這樣:

// 把輸入引數塞給自己,類似於生產操作
public void pushList(List<? extends T> t) {
    list.addAll(t);
}

即編譯器認為,List<Integer> 和List<? extend Number>是一個東西,允許!

Q: 如果要支援pollList,怎麼修改定義?
A:

// 把自己的內容塞給輸入引數,類似於讓輸入引數做消費。
public void pollList(List<? super T> t) {
    t.addAll(list);
}

因為是把自己的東西塞給輸入引數, 而想要能塞進去,必須保證自己這個T,是輸入引數的子類,反過來說,輸入引數必須是T的父類,所以用super
於是編譯器認為,List<Object> 和List<? super Number>是一個東西,允許!

PECS原則出自Effective Java, 注意只是一個程式設計建議而已!

  • 如果有一個類A,泛型引數為T
  • 如果他一般只用於接收輸入容器List後,塞入自己內部的T容器, 則類A就叫生產者, 因此輸入引數最好定義為<? extend T>最好, 以便能接收任何T子類的容器。
  • 如果他一般只用於接收輸入容器後List, 把自己內部的T元素塞給它, 那麼這個類A就叫消費者, 輸入引數最好定義為<? super T>\ 最好, 以便自己的T元素能塞給任何T元素的父類容器。

 本文分享自華為雲社群《15個問題掌握java泛型》,原文作者:breakDraw 。

點選關注,第一時間瞭解華為雲新鮮技術~

相關文章