一文帶你看懂java 泛型,史上最全面的泛型教學啦

一個老農程序員 發佈 2019-12-23T10:10:12+00:00

認真看這篇文章,保證你們對泛型又有新的理解,如果沒有的話,請順著網線來打我呀。概述引用下百度百科的回答泛型是程序設計語言的一種特性。允許程式設計師在強類型程序設計語言中編寫代碼時定義一些可變部分,那些部分在使用前必須作出指明。各種程序設計語言和其編譯器、運行環境對泛型的支持均不一樣。

認真看這篇文章,保證你們對泛型又有新的理解,如果沒有的話,請順著網線來打我呀。

概述

引用下百度百科的回答

泛型是程序設計語言的一種特性。允許程式設計師在強類型程序設計語言中編寫代碼時定義一些可變部分,那些部分在使用前必須作出指明。各種程序設計語言和其編譯器、運行環境對泛型的支持均不一樣。將類型參數化以達到代碼復用提高軟體開發工作效率的一種數據類型。泛型類是引用類型,是堆對象,主要是引入了類型參數這個概念。

我的理解是:泛型就是把類型明確的工作推遲到創建對象或調用方法的時候才去明確的特殊的類型。

參數化類型,把類型當作是參數一樣傳遞,Object<數據類型>這裡面只能是引用類型不能是基本類型。

比如:

Object<Integer>  //true
Object<int> //false


為什麼泛型裡面數據類型不能是基本類型呢?

因為虛擬機在編譯時會把帶泛型的轉換成Object類型,而基本類型不屬於Object類型,所以泛型裡面數據類型不能是基本類型。


為什麼要使用泛型呢?

Java語言引入泛型的好處是安全簡單。泛型的好處是在編譯的時候檢查類型安全,並且所有的強制轉換都是自動和隱式的,提高代碼的重用率。


有一點很重要就是消除了強制類型轉換,減少了出錯機會,舉個例子:

public class Test {
    public static void main(String[] args){
        List list = new ArrayList();
        list.add("1"); 
        list.add(1);
        int i = (int)list.get(0); // java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
        int j = (int)list.get(1);
    }
}

上面代碼在編譯期沒有問題,因為list的add方法是Object類型,所以在編譯期沒有什麼問題,但是運行期的時候我們調用 list的時候並不知道list裡面具體存了什麼類型的參數,所以取的時候有可能就會報類型轉換的錯誤 。

如果用了泛型上面的錯誤就不會發生了。

public class Test {
    public static void main(String[] args){
        List<String> list = new ArrayList();
        list.add("1");
        list.add(1);//在編譯期就會出錯,因為用了泛型,虛擬機就會在編譯期的時候檢查泛型類型安全。
    }
}

泛型只存在於編譯期。

舉個例子。

public class Test {
    public static void main(String[] args) throws Exception{
        List<String> list = new ArrayList();
        list.add("hello");
        //list.add(23) //編譯期會報錯
        Class c = Class.forName("java.util.ArrayList");
        Method m = c.getMethod("add",Object.class);
        m.invoke(list,23);
        System.out.println(list); // [hello, 23]
    }
}

通過上面可知,泛型只在編譯期有效,為什麼運行期失效了呢,這是因為泛型的擦除概念,通俗點來說就是泛型的信息不會進行運行階段。

泛型的使用

泛型有三種實用方式

泛型類:public class Test<T>}{} T表示未知類型

泛型接口:public interface Test<T>{} 和定義類一樣

泛型方法:public <T> void Test(T name){}

泛型類的使用

泛型類在java中有著很重要的地位,其中我們用的最多的就是ArrayList,HashMap,HashSet.

既然是帶你看懂Java泛型,肯定不能源碼裡面的那些容器類呀,那些容器類都已經很完善了如果要帶你們看的話,肯定會越看越暈的,所以我們自己定義一個泛型類出來。

//這個T可以換成隨便一個字母 ,只不過我寫泛型都用的T,你可以換成A,B,C...
public class Test<T> {
    T name;
    public Test(T name){
        this.name = name;
    }
    public T getName() {
        return name;
    }

    public void setName(T name) {
        this.name = name;
    }

//如果不傳泛型類型的話,那麼默認的就是Object型什麼都可以傳
Test test = new Test("hello");
//傳入的數據類型不為基本類型,否則編譯期會報錯,開頭我解釋過為什麼會報錯了
Test<Integer> test1 = new Test<>(418);

泛型接口的使用

泛型接口的定義和泛型類的定義差不多,我們常見的泛型接口就是,List,Map,Set.

首先老規矩我們自己定義一個泛型接口。

public interface Test<T>{
    T getName(T name);
}

//如果實現接口的時候不傳入數據類型的話,需要將泛型聲明也要寫到類中要不然會報錯
class Test1<T> implements Test<T>{

    @Override
    public T getName(T name) {
        return null;
    }
}

//實現接口的時候傳入數據類型的話,就不用把泛型聲明也寫到類中了
class Test2 implements  Test<String>{

    @Override
    public String getName(String name) {
        return name;
    }
}

泛型方法的使用

泛型方法的使用

public <T> void getName(T name){} 
public <T,K> void getNameAndValue(T name, K value){}
public <T,K,V> void getNameAndValueAndV(T name, K value, V v){}//總的來說就是參數需要多少泛型,返回值前面就得定義幾個泛型要不然編譯期會出錯

泛型通配符

為什麼要用通配符呢?

java裡面類和類之間是有繼承關係 的,比如Cat extends Animals,那麼Cat就是Animal的子類,但是集合是沒有繼承這個概念的,比如List<Cat> catList和List<Animals> animalList你不能說 animalList是catList的父類,所以很難看出來這兩個類之間的聯繫,但是我們現在只想讓list裡面只加入Animals的子類怎麼辦呢?

  1. 一種是Animals有多少個子類就定義多少個list,這種方法雖然也可以實現但是Animals如果有一百個,一千個,一萬個子類呢你這種方法是不是就太耗時了呢。
  2. 第二種就是用通配符來實現。比如:List animals 這個時候animals就只能添加Animals的子類了,一個list搞定。


通配符的基本概念?

  1. 無邊界的通配符:? 舉個例子,能接收所有未知類型的泛型
public class Test {
 public static void main(String []args){
      List<Integer> list = new ArrayList<>();
      list.add(1);
      list.add(2);
      list.add(3);
      list.add(4);
      List<String> stringList = new ArrayList<>();
      stringList.add("h");
      stringList.add("e");
      stringList.add("l");
      stringList.add("l");
      stringList.add("o");
      getList(stringList);
      getList(list);
  }
 //無論傳入什麼List都會被接收
 public static List getList(List<?> list){
 return list;
  }


用List聲明的List 不能使用add方法,因為你不知道的類型是什麼,但是list.add(null)就可以,因為null是所有類型都有的。舉個例子

public static List getList(List<?> list){
   // list.add(1);//會報參數不匹配的錯誤,編譯期報錯
   // list.add("hello");//會報參數不匹配的錯誤,編譯期報錯
      list.add(null);//添加成功
      return list;
}


用get方法也只能用Object來接收,因為你不知道你的類型是什麼。

public static List getList(List<?> list){
 int i = list.get(0); //編譯期報錯
   String j = list.get(1); //編譯期報錯
   Object o = list.get(3); //運行正確
 return list;
}


  1. 上邊界通配符號:可以接收E以及E的子類型的泛型,這裡面的E不止是類哦,也可以是接口,看個例子。
//這個是繼承了類的用法
 public class Test {
 public static void main(String[] args) {
       List<Integer> list = new ArrayList<>();
       list.add(1);
       getList(list);
       List<String> strings = new ArrayList<>();
       strings.add("hello");
       getList(strings);//編譯期報錯
   }

 public static List getList(List<? extends Number> list) {
 return list;
   }
}

public class Test {
 public static void main(String[] args) {
       List<Integer> list = new ArrayList<>();
       list.add(1);
       getList(list);// 編譯期報錯
       List<Test2> test2s = new ArrayList<>();
       getList(test2s);
   }
 //上邊界為接口的實現,只要是實現了此接口的類都可以被當做泛型傳進來
 public static List getList(List<? extends Test1> list) {
 return list;
   }
}

interface Test1{

}
class Test2 implements Test1{}


以上可知上邊界就是你傳入的類型必須得是E的子類,或者是實現接口的類。

  1. 下邊界通配符號: 就是傳入的類型必須得是E以及E的父類,舉個例子
public class Test {
 public static void main(String[] args) {
 List<Animals> animals = new ArrayList<>();
       getList(animals); 
 List<Cat> cats = new ArrayList<>();
       getList(cats);
 List<Dog> dogs = new ArrayList<>();
       getList(dogs);//編譯出錯,因為Dog不是Cat的父類

   }

 public static List getList(List<? super Cat> list) {
 return list;
   }
}
class Animals{}
class Cat extends Animals{}
class Dog extends Animals{}

The End

我覺得在java裡面我們用的最多的泛型就是List,Map這兩個了吧,其實我們自己編程的時候多用用泛型,可以減少一定的代碼量,還能讓代碼看起來更好看 ,用雷軍的一句話說,別人都說我寫的代碼像詩一樣,希望你們能在java的路上越走遠啊。


作為一個程式設計師,技術不是唯一出路,但技術也是非常重要的,現在競爭越來越激烈,只有不斷的進階才是我們的出路

編程是一個神秘而又美麗的領域,這個領域的大門像著所有擁有理想的人們敞開。編程也是一個殘酷而又充滿競爭的領域,隨時會淘汰掉不夠努力的人


最後我自己是一名從事了多年開發的JAVA老程式設計師,今年年初我花了一個月整理了一份最適合2019年學習的java學習乾貨,想分享給每一位喜歡java的小夥伴,需要獲取的可以關注我並在後台私信我:01,即可免費領取。


關鍵字: