首頁  >  文章  >  Java  >  如何使用Java泛型方法

如何使用Java泛型方法

王林
王林轉載
2023-05-01 14:16:062598瀏覽

泛型方法

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

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

三種泛型參數推斷方式:

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

fTest.<integer>f(xxx)</integer>

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

int number = 0;
fTest.f(number)

3、可由 ##回傳值 確定

List<integer> list = fTest.f(xxx);</integer>
Q: 下面這段程式碼哪裡有問題?是toString()那裡嗎?

public class A<t> {
	public static void  test(T t){
  		System.out.println(t.toString());  
  }
}</t>
A:test是static方法, 因此無法感知A實例裡的T

需要改成
public static 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) {
	  ...
   }
}</t>
A: 不能,編譯器會報錯。

Q: 泛型參數T可以進行new T()或new T[]操作嗎?

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

如何使用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>()</child></parent>
A:有問題,編譯就錯了。 List和ArrayList不存在父子類別的關係

Q:

List extends Parent> list = new ArrayList<child>();</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>();</parent>
這個list有什麼特色?

下面誰會報錯

list.add(new Child())
list.add(new Parent())
Parent a= list.get();
Child b = list.get()
A:截圖如下:

如何使用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>();</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);</apple></fruit>
A:不會報錯。會正常列印結果。

如何使用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></t></t></t>
則T就是泛型參數。

Q:下面程式碼能正常運作嗎?

MyList<number> myList = new MyList();

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

List<object> objectList = new ArrayList();
myList.pollList(objectList);</object></integer></number>
A:不能正常運行, pushList和pollList都會報錯

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

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

A:改成這樣:

// 把输入参数塞给自己,类似于生产操作
public void pushList(List extends T> t) {
    list.addAll(t);
}
即編譯器認為,List 和List extend Number>是一個東西,允許!

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

A:

// 把自己的内容塞给输入参数,类似于让输入参数做消费。
public void pollList(List super T> t) {
    t.addAll(list);
}

因为是把自己的东西塞给输入参数, 而想要能塞进去,必须保证自己这个T,是输入参数的子类,反过来说,输入参数必须是T的父类,所以用super
于是编译器认为,List 和List super Number>是一个东西,允许!

PECS原则出自Effective Java, 注意只是一个编程建议而已!

  • 如果有一个类A,泛型参数为T

  • 如果他一般只用于接收输入容器List后,塞入自己内部的T容器, 则类A就叫生产者, 因此输入参数最好定义为 extend T>最好, 以便能接收任何T子类的容器。

  • 如果他一般只用于接收输入容器后List, 把自己内部的T元素塞给它, 那么这个类A就叫消费者, 输入参数最好定义为 super T>\ 最好, 以便自己的T元素能塞给任何T元素的父类容器。

以上是如何使用Java泛型方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除