java 泛型使用詳解
1、概述
泛型是Java SE 1.5的新特性,泛型的本質是引數化型別,也就是說所操作的資料型別被指定為一個引數。在使用的時候,具體指定操作資料型別,這種引數型別可以用在類、介面和方法的建立中,分別稱為泛型類、泛型介面、泛型方法。
在Java SE 1.5之前,沒有泛型的情況的下,通過對型別Object的引用來實現引數的“任意化”,“任意化”帶來的缺點是要做顯式的強制型別轉換,而這種轉換是要求開發者對實際引數型別可以預知的情況下進行的。對於強制型別轉換錯誤的情況,編譯器可能不提示錯誤,在執行的時候才出現異常,這是一個安全隱患。
泛型的好處是在編譯的時候檢查型別安全,並且所有的強制轉換都是自動和隱式的,以提高程式碼的重用率。
2、泛型舉例
例子1:
//泛型例子
public static void testGenerics(){
ArrayList list = new ArrayList();
list.add("nihao");
list.add(100);
for(int i =0;i<list.size();i++){
String content = (String)list.get(i);
System.out.println(content);
}
}
上述程式碼編譯可以正常通過,但執行會報一下錯誤:
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
說明:
因為ArrayList內部儲存資料用的是Object陣列,所以可以向ArrayList新增任意型別資料(String,Integer),所以在編譯期間可以正常通過。但是再執行期間,由於添加了Integer資料,強制轉換成String使用的時候就會報錯。為了在編譯期間(編譯器做出錯誤提示)就可以解決此類問題,泛型便出現了。
我們將上述ArrayList 的宣告初始化程式碼修改如下,編譯器會在編譯階段就會發現類似這樣的問題。
如上圖,可以發現,eclipse做出了錯誤提醒。
例子2:
public static void testRunGenerics(){
List<String> list1 = new ArrayList<String>();
List<Long> list2 = new ArrayList<Long>();
System.out.println(list1.getClass().equals(list2.getClass()));
}
執行結果:true
上述例子說明,java程式碼在編譯後,會去除泛型資訊。也即是說,java中泛型只是在編譯階段有效。在編譯過程中,正確檢驗泛型結果後,會將泛型相關資訊擦除,並在物件進入和離開方法的邊界處,新增型別檢查,和型別轉換的方法。
一句話:泛型型別,只在編譯階段提供,型別資訊,用於檢查型別安全,和生成型別轉換方法。編譯成的class檔案,其實都是相同的基本型別。
3、泛型的定義和使用
泛型有三種使用方式,泛型介面,泛型類,泛型方法;
泛型介面
Show.java檔案
/**
*
*/
package generics;
/**
* @author chzhao
*
*/
public interface Show <T,M>{
public void show(T t,M m);
}
TestGeneric.java檔案
package generics;
import java.util.Date;
//在類的定義的時候指定第一個引數為String,另一個引數不指定任何型別
class ShowTest <M> implements Show<String,M>{
@Override
public void show(String t, M m) {
// TODO Auto-generated method stub
System.out.println(t);
System.out.println(m);
}
}
public class TestGeneric {
public static void main(String[] args) {
//可以在使用的時候指定第二個引數型別。
//注意介面Show中指定引數,第一個引數必須和
//ShowTest定義指定的第一個引數型別相同,如不同編譯器報錯。
Show <String,Date>showTest = new ShowTest<Date>();
Date date = new Date();
showTest.show("nihao", date);
}
}
執行結果:
nihao
Sat Feb 24 15:01:17 CST 2018
泛型類
在類的定義中使用泛型,我們稱這樣的類為泛型類。jdk中最典型的的泛型類,如List Set Map。
泛型類的基本寫法:
class 類名稱 <泛型標識:可以隨便寫任意標識號,標識指定的泛型的型別>{
private 泛型標識 /*(成員變數型別)*/ var;
.....
}
}
TestGeneric.java檔案
package generics;
import java.util.Date;
//在例項化泛型類時,必須指定V的具體型別
//此處V可以寫成任意符合java規則的標識
class GenericValue <V>{
//value這個成員變數的型別為V,V的型別由外部指定
private V value;
//泛型構造方法,形參v的型別由該類具體使用時指定。
public GenericValue(V v){
this.value = v;
}
//泛型方法 返回值由該類具體使用時指定。
public V getValue(){
return value;
}
}
public class TestGeneric {
public static void main(String[] args) {
GenericValue<String> genericValue = new GenericValue<String>("weiwei");
System.out.println(genericValue.getValue());
GenericValue<Date> genericValue1 = new GenericValue<Date>(new Date());
System.out.println(genericValue1.getValue());
}
}
執行結果:
weiwei
Sat Feb 24 15:26:25 CST 2018
泛型類使用,記住一點在使用的時候(宣告和例項化的時候)具體指定引數型別。
還有一點說明,定義了一個泛型類,我們在使用的時候指定具體型別,主要作用給編譯器在編譯java原始碼的時候使用,可以起到型別安全檢查,和型別強制轉換程式碼的生成。但記住我們在使用的時候,也可以不指定具體型別(其實也就是放棄了使用泛型,也就享受不到泛型帶來的好處了),這時候,程式碼也是可以編譯通過執行的,如下面例子。
public class TestGeneric {
public static void main(String[] args) {
GenericValue genericValue = new GenericValue("weiwei");
System.out.println(genericValue.getValue());
GenericValue genericValue1 = new GenericValue(new Date());
System.out.println(genericValue1.getValue());
}
}
執行結果:
weiwei
Sat Feb 24 15:26:25 CST 2018
泛型方法
/**泛型方法
* @param T 宣告一個泛型型別
* @param c 用來建立泛型物件
* @return T 返回一個泛型型別的返回值
* @throws InstantiationException
* @throws IllegalAccessException
*/
//<T> 此作用說明該方法為泛型方法
//引數(Class<T>)指定此方法的具體泛型型別
public <T> T getObject(Class<T> c) throws InstantiationException, IllegalAccessException{
T t = c.newInstance();
return t;
}
測試程式碼TestGeneric.java檔案
package generics;
import java.util.Date;
class Peoples{
private String name = "張三";
public void showName(){
System.out.println(name);
}
}
//在例項化泛型類時,必須指定V的具體型別
//此處V可以寫成任意符合java規則的標識
class GenericValue <V>{
//value這個成員變數的型別為V,V的型別由外部指定
private V value;
//泛型構造方法,形參v的型別由該類具體使用時指定。
public GenericValue(V v){
this.value = v;
}
//泛型方法 返回值由該類具體使用時指定。
public V getValue(){
return value;
}
/**泛型方法
* @param T 宣告一個泛型型別
* @param c 用來建立泛型物件
* @return T 返回一個泛型型別的返回值
* @throws InstantiationException
* @throws IllegalAccessException
*/
public <T> T getObject(Class<T> c) throws InstantiationException, IllegalAccessException{
T t = c.newInstance();
return t;
}
}
public class TestGeneric {
public static void main(String[] args) {
GenericValue <String>genericValue = new GenericValue<String>("weiwei");
try {
Peoples people = genericValue.getObject(Peoples.class);
people.showName();
} catch (InstantiationException | IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
執行結果:張三
靜態泛型方法
import java.util.ArrayList;
import java.util.List;
public class TestGeneric {
//列印容器泛型方法
public static <T> void printList(List<T> t){
for(T tt:t){
System.out.print(tt+"\t");
}
System.out.println();
}
public static void main(String[] args) {
List<Integer> intList = new ArrayList<Integer>();
List<String> stringList = new ArrayList<String>();
for(int i =0;i<10;i++){
intList.add(i);
stringList.add("weiwei"+i);
}
//列印list容器
printList(intList);
printList(stringList);
}
}
執行結果:
泛型方法能使方法獨立於類而產生變化,對於泛型方法的使用有一個指導原則:如果你能做到,你就該儘量使用泛型方法。也就是說,如果使用泛型方法將整個類泛型化,那麼就應該使用泛型方法。對於一個static的方法,無法訪問泛型型別的引數。所以如果static方法要使用泛型能力,就必須使其成為泛型方法
希望對您有所幫助!