java泛型方法 通配符_Java泛型教程–示例类,接口,方法,通配符等
java泛型方法 通配符
泛型是Java編程的核心功能之一,它是Java 5中引入的。如果您使用的是Java Collections ,并且版本5或更高版本,則可以肯定使用了它。 將泛型與集合類一起使用非常容易,但是它提供了比僅創建集合類型更多的功能,我們將在本文中嘗試學習泛型的功能。 如果我們使用專業術語,對泛型的理解有時會變得混亂,因此我將盡量保持其簡單易懂。
在本教程中,我們將研究泛型的以下主題。
Java泛型示例
Java 5中添加了泛型,以提供編譯時類型檢查并消除使用集合類時常見的ClassCastException風險。 整個收集框架都進行了重寫,以使用泛型進行類型安全。 讓我們看看泛型如何幫助我們安全地使用集合類。
List list = new ArrayList(); list.add("abc"); list.add(new Integer(5)); //OKfor(Object obj : list){String str=(String) obj; //type casting leading to ClassCastException at runtime }上面的代碼可以很好地編譯,但是在運行時拋出ClassCastException,因為我們試圖將列表中的Object強制轉換為String,而其中一個元素是Integer類型。 在Java 5之后,我們使用如下收集類。
List<String> list1 = new ArrayList<String>(); // java 7 ? List<String> list1 = new ArrayList<>(); list1.add("abc"); //list1.add(new Integer(5)); //compiler errorfor(String str : list1){//no type casting needed, avoids ClassCastException }請注意,在創建列表時,我們已指定列表中元素的類型為String。 因此,如果我們嘗試在列表中添加任何其他類型的對象,則該程序將引發編譯時錯誤。 還要注意,在for循環中,我們不需要類型轉換列表中的元素,因此在運行時刪除了ClassCastException。
具有類和接口的泛型
我們可以使用泛型類型定義自己的類和接口。 泛型類型是通過類型進行參數化的類或接口。 我們使用尖括號(<>)來指定type參數。
為了了解其好處,可以說我們有一個簡單的類:
package com.journaldev.generics;public class GenericsTypeOld {private Object t;public Object get() {return t;}public void set(Object t) {this.t = t;}public static void main(String args[]){GenericsTypeOld type = new GenericsTypeOld();type.set("Pankaj"); String str = (String) type.get(); //type casting, error prone and can cause ClassCastException} }請注意,在使用此類時,我們必須使用類型轉換,并且它可以在運行時產生ClassCastException。 現在,我們將使用泛型來重寫具有泛型類型的相同類,如下所示。
package com.journaldev.generics;public class GenericsType<T> {private T t;public T get(){return this.t;}public void set(T t1){this.t=t1;}public static void main(String args[]){GenericsType<String> type = new GenericsType<>();type.set("Pankaj"); //validGenericsType type1 = new GenericsType(); //raw typetype1.set("Pankaj"); //validtype1.set(10); //valid and autoboxing support} }注意main方法中GenericsType類的使用。 我們不需要進行類型轉換,并且可以在運行時刪除ClassCastException。 如果我們在創建時未提供類型,則編譯器將發出警告,“ GenericsType是原始類型。 泛型類型GenericsType <T>的引用應參數化”。 當我們不提供類型時,該類型將成為Object ,因此它同時允許String和Integer對象,但是我們應始終嘗試避免這種情況,因為在處理會產生運行時錯誤的原始類型時,我們將必須使用類型轉換。
提示 :我們可以使用@SuppressWarnings("rawtypes")注釋來取消編譯器警告,請參閱Java注釋教程 。
還要注意,它支持Java自動裝箱 。
可比接口是接口中泛型的一個很好的例子,它寫為:
package java.lang; import java.util.*;public interface Comparable<T> {public int compareTo(T o); }以類似的方式,我們可以在接口和類中使用泛型。 我們也可以像在Map界面中那樣具有多個類型參數。 同樣,我們也可以為參數化類型提供參數化值,例如new HashMap<String, List<String>>(); 已驗證。
泛型類型命名約定
命名約定可以幫助我們輕松地理解代碼,擁有命名約定是Java編程語言的最佳實踐之一。 因此,泛型也帶有它自己的命名約定。 通常,類型參數名稱是單個大寫字母,以使其易于與Java變量區分開。 最常用的類型參數名稱為:
- E –元素(由Java Collections Framework廣泛使用,例如ArrayList,Set等)
- K –鍵(在地圖中使用)
- N –數字
- T –類型
- V –值(在地圖中使用)
- S,U,V等–第二,第三,第四類型
方法和構造函數中的泛型
有時我們不希望對整個類進行參數化,在這種情況下,我們也可以在方法中使用泛型類型。 由于構造函數是一種特殊的方法,因此我們也可以在構造函數中使用泛型類型。
這是一個類,顯示方法中的泛型類型的示例。
package com.journaldev.generics;public class GenericsMethods {//Generics in methodpublic static <T> boolean isEqual(GenericsType<T> g1, GenericsType<T> g2){return g1.get().equals(g2.get());}public static void main(String args[]){GenericsType<String> g1 = new GenericsType<>();g1.set("Pankaj");GenericsType<String> g2 = new GenericsType<>();g2.set("Pankaj");boolean isEqual = GenericsMethods.<String>isEqual(g1, g2);//above statement can be written simply asisEqual = GenericsMethods.isEqual(g1, g2);//This feature, known as type inference, allows you to invoke a generic method as an ordinary method, without specifying a type between angle brackets.//Compiler will infer the type that is needed} }注意isEqual方法簽名顯示了在方法中使用泛型類型的語法。 還要注意如何在我們的java程序中使用這些方法。 我們可以在調用這些方法時指定類型,也可以像普通方法一樣調用它們。 Java編譯器足夠聰明,可以確定要使用的變量的類型,這種功能稱為類型推斷 。
泛型有界類型參數
假設我們想限制可以在參數化類型中使用的對象的類型,例如,在比較兩個對象的方法中,并且我們要確保接受的對象是可比較的。 要聲明一個有界的類型參數,請列出該類型參數的名稱,然后是extends關鍵字,然后是其上限,類似于下面的方法。
public static <T extends Comparable<T>> int compare(T t1, T t2){return t1.compareTo(t2);}這些方法的調用與無界方法類似,不同之處在于,如果我們嘗試使用任何非Comparable的類,則會引發編譯時錯誤。
綁定類型參數可以與方法以及類和接口一起使用。
泛型也支持多個范圍,即<T擴展A&B&C>。 在這種情況下,A可以是接口或類。 如果A是類,則B和C應該是接口。 在多個范圍內,我們不能有多個類。
泛型與繼承
我們知道,如果A是B的子類,則Java繼承允許我們將變量A分配給另一個變量B。因此,我們可能認為可以將A的任何泛型類型分配給B的泛型類型,但事實并非如此。 讓我們用一個簡單的程序看看。
package com.journaldev.generics;public class GenericsInheritance {public static void main(String[] args) {String str = "abc";Object obj = new Object();obj=str; // works because String is-a Object, inheritance in javaMyClass<String> myClass1 = new MyClass<String>();MyClass<Object> myClass2 = new MyClass<Object>();//myClass2=myClass1; // compilation error since MyClass<String> is not a MyClass<Object>obj = myClass1; // MyClass<T> parent is Object}public static class MyClass<T>{}}我們不允許將MyClass <String>變量分配給MyClass <Object>變量,因為它們不相關,實際上MyClass <T>的父對象是Object。
通用類和子類型
我們可以通過擴展或實現來泛型一個通用類或接口。 一個類或接口的類型參數與另一類或接口的類型參數之間的關系由extend和Implements子句確定。
例如,ArrayList <E>實現擴展Collection <E>的List <E>,因此ArrayList <String>是List <String>的子類型,而List <String>是Collection <String>的子類型。
只要不更改type參數,子類型關系就會保留,下面顯示了多個type參數的示例。
interface MyList<E,T> extends List<E>{ }List <String>的子類型可以是MyList <String,Object>,MyList <String,Integer>等。
通用通配符
問號(?)是泛型中的通配符,表示未知類型。 通配符可以用作參數,字段或局部變量的類型,有時還可以用作返回類型。 在調用通用方法或實例化通用類時,我們不能使用通配符。 在以下各節中,我們將學習上界通配符,下界通配符和通配符捕獲。
泛型上界通配符
上限通配符用于放松方法中對變量類型的限制。 假設我們要編寫一個將返回列表中數字總和的方法,那么我們的實現將是這樣的。
public static double sum(List<Number> list){double sum = 0;for(Number n : list){sum += n.doubleValue();}return sum;}現在,上述實現的問題在于它不適用于List of Integers或Doubles,因為我們知道List <Integer>和List <Double>不相關,這在使用上限通配符很有用時。 我們將泛型通配符與extends關鍵字和上限類或接口一起使用,這將允許我們傳遞上限或其子類類型的參數。
可以像下面的程序一樣修改上面的實現。
package com.journaldev.generics;import java.util.ArrayList; import java.util.List;public class GenericsWildcards {public static void main(String[] args) {List<Integer> ints = new ArrayList<>();ints.add(3); ints.add(5); ints.add(10);double sum = sum(ints);System.out.println("Sum of ints="+sum);}public static double sum(List<? extends Number> list){double sum = 0;for(Number n : list){sum += n.doubleValue();}return sum;} }就像按照接口編寫代碼一樣,在上面的方法中,我們可以使用上限類Number的所有方法。 請注意,對于上界列表,除null之外,我們不允許將任何對象添加到列表中。 如果我們嘗試在sum方法內將元素添加到列表中,則該程序將無法編譯。
泛型無界通配符
有時,我們希望通用方法適用于所有類型,在這種情況下,可以使用無界通配符。 與使用<?相同 擴展Object>。
public static void printData(List<?> list){for(Object obj : list){System.out.print(obj + "::");}}我們可以為printData方法提供List <String>或List <Integer>或任何其他類型的Object列表參數。 與上限列表類似,我們不允許在列表中添加任何內容。
泛型下界通配符
假設我們要在方法中將整數添加到整數列表中,我們可以將參數類型保持為List <Integer>,但它將與Integers捆綁在一起,而List <Number>和List <Object>也可以容納整數,因此我們可以使用下限通配符來實現此目的。 我們使用帶有super關鍵字和下限類的泛型通配符(?)來實現此目的。
在這種情況下,我們可以傳遞下界或下界的任何超級類型作為參數,java編譯器允許將下界對象類型添加到列表中。
public static void addIntegers(List<? super Integer> list){list.add(new Integer(50));}使用泛型通配符進行子類型化
List<? extends Integer> intList = new ArrayList<>(); List<? extends Number> numList = intList; // OK. List<? extends Integer> is a subtype of List<? extends Number>類型擦除
添加了泛型以在編譯時提供類型檢查,并且在運行時無用,因此Java編譯器使用類型擦除功能來刪除字節碼中的所有泛型類型檢查代碼,并在必要時插入類型轉換。 類型擦除可確保不會為參數化類型創建新的類; 因此,泛型不會產生運行時開銷。
例如,如果我們有如下通用類;
public class Test<T extends Comparable<T>> {private T data;private Test<T> next;public Test(T d, Test<T> n) {this.data = d;this.next = n;}public T getData() { return this.data; } }Java編譯器用第一個綁定接口Comparable替換了綁定類型參數T,如下代碼:
public class Test {private Comparable data;private Test next;public Node(Comparable d, Test n) {this.data = d;this.next = n;}public Comparable getData() { return data; } } 多數民眾贊成在Java中的泛型,泛型是一個非常大的話題,需要大量時間才能有效地理解和使用它。 本文旨在提供泛型的基本細節,以及如何使用泛型來擴展程序的類型安全性。
翻譯自: https://www.javacodegeeks.com/2013/07/java-generics-tutorial-example-class-interface-methods-wildcards-and-much-more.html
java泛型方法 通配符
總結
以上是生活随笔為你收集整理的java泛型方法 通配符_Java泛型教程–示例类,接口,方法,通配符等的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 英语学习网站3
- 下一篇: 思科网络技术学院2002年会(上)(转)