資安

Java泛型初探

前言

在學習java掉頭的日子裡很多青年脫坑,同時也有很多青年入坑,但入坑的時候可能沒有什麼好的指導或者學習方法可能頭髮掉的一發不可收拾……

筆者有個學弟就遇到了相同的境遇,學弟被泛型搞得頭暈目眩,搞不懂泛型是個啥玩意。天天用的泛型也不知道啥玩意(他可能都不知道他有沒有用泛型)。立圖為證!當然,筆者深度還欠缺,如果錯誤還請指正!

在這裡插入圖片描述

本篇就根據筆者的理解簡單的介紹一下泛型(深入還需自己),如果深度不夠或者有錯誤還請見諒。

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

沒有泛型的時候

泛型,從字面的意思來看,廣泛的意思,能夠廣泛的代表或者處理某一類事務的類型(java集合類)。在沒有泛型的時候,你會如何去處理?比如你寫鏈表的時候。可能會這樣:

public class node {
    public int value;//節點的結果
    node next;//下一個連接的節點
    public node(){}
    public node(int value)
    {
        this.value=value;
    }
}

這個node 節點存的是int類型,如果是存一個字符串的鏈表或者是一個double類型數據鏈表呢?難道要這樣重複的寫:

public class node {
    public String value;//節點的結果
    node next;//下一個連接的節點
    public node(){}
    public node(String value)
    {
        this.value=value;
    }
}
  • 重寫?你去重寫吧。

在這裡插入圖片描述

使用Object類表示泛型

發現在設計上存在的這個大問題之後,大大紛紛考慮到這種問題的嚴重和複雜性,隨著面向對象的發展流行我們知道在java中有向上轉型向下轉型.

向上轉型:將子類對象賦值給父類類型的變量,這種技術稱為向上轉型。可以在父類中定義方法,然後根據子類中具體實現這樣也正是多態機制的基本思想。
在這裡插入圖片描述
因為很多時候我們不一定使用類越細越好,比如狗的共同點已經很相似了,一群狗我們可能使用dog來表示操作它們,但是在實例化可能根據哈士奇、藏獒、金毛等不同種類具體實例化。重寫部分特殊函數,在使用的時候直接使用dog的api即可滿足要求。

在java中這種多態思想也非常多,比如類似以下:

List<Integer>list1=new ArrayList<Integer>();
List<Integer>list2=new LinkedList<Integer>();

前面的向上轉型是更具體的類轉為較為抽象的類。談完向上轉型,當然還有個向下轉型啦,向下轉型就是將較抽象的類轉換為較具體的類。當然向下轉型需要強制類型轉換(顯示轉換告訴編譯器)
在這裡插入圖片描述
如果使用代碼來看:

public class test {
    public static void main(String[] args) {
       //animal指想dog的堆地址,向上轉型 從 dog——》animal 向上父類轉
        animal animal=new dog("doudou",17);
        //dog 指想animal指想的地址,從 anmial——》dog為向下子類轉
        dog dog=(dog)animal;
        animal.sayhello();
        dog.sayhello();
    }
}

class  animal
{
    public String name;
    public  int age;
    public animal(String name,int age){
        this.name=name;
        this.age=age;
    }
    public  void sayhello()
    {
        System.out.println("hello,我是aninal"+this.name+"今年"+this.age+"歲");
    }
}
class dog extends  animal
{
    public dog(String name, int age) {
        super(name, age);
    }
    @Override
    public void sayhello() {
        System.out.println("hello,我是狗狗"+this.name+"今年"+age+"歲");
    }
}

我們知道在java中Object類為所有類的父類(超類)。它是站在最頂端的類型,所有類(class)都是它的子子孫孫,它自己寫好了toString(),equalls()等方法。

而同理我們借鑑這種思想可以將一個類先向上轉型成Object類,然後再將操作完的數向下轉型成我們所需要的數。達到這種使用上的效果,但是基本類型無法滿足這個要求啊,所以就出現了包裝類這個東西。在儲存對象時候一定程度上能夠滿足使用需求。

public class test {
    public static void main(String[] args) {
        Integer a=10;
        node node1=new node(a);
        Integer va1=(Integer) node1.getValue();
        System.out.println(va1);

        node node2=new node("hello");
        String va2=(String) node2.getValue();
        System.out.println(va2);
    }
}
class node
{
    private Object value;
    public  node(Object value)
    {
        this.value=value;
    }
    public Object getValue() {
        return value;
    }
    public void setValue(Object value) {
        this.value = value;
    }
}

執行的結果為:

10
hello

這種雖然一定程度能夠達到泛型效果,但是除了功能受限、使用麻煩之外,還有個更大的毛病。就是在第二次顯示的向下轉向的時候,如果人為轉換錯誤編譯器無法識別,而必須等到代碼執行時候才報錯,這樣的機制讓java代碼變得不太安全。
在這裡插入圖片描述

Java泛型

在Object顯示轉換存在不安全行為的情況下,Java在jdk1.5以後提出了泛型機制,通過泛型就能有效避免轉型時候出現的問題,泛型簡單的理解就是在類、接口、方法中定義未知類型變量,只有初始化的時候才知道真正的類型。在定義的類或接口函數中可以直接使用這個未知類型進行操作。

泛型類

泛型類的語法如下:

類名<T>  其中T代表一個類型名稱
類名<T1,T2> 可能有多個類型

其中T,T1,T2都叫做通配符。常用的通配符有T,E,K,V分別表示類型、元素、鍵、值,當然這並不是硬性規定,而是大家形成的一種通識。

你在創建node類就可以使用如下的寫法了:

public class node <T>{
    private T value;
    public node(T value) {
        this.value = value;
    }
    public T getValue() {
        return value;
    }
    public void setValue(T value) {
        this.value = value;
    }
}

其中T就可以在類中自由使用,這個類我們暫時不知道是什麼,但是初始化的時候編譯器就知道它是什麼類型:
在這裡插入圖片描述

泛型接口

既然類可以使用泛型,接口當然也可以,不過接口使用泛型和普通類的略有區別,子類在繼承泛型接口的時候需要接口處聲明泛型類型,否則編譯器報錯。例如下面的pig類。

而如果你依然想在子類中使用泛型,那就需要在子類中聲明一個泛型,而接口中的泛型使用子類的泛型類型。例如下面的dog類。

interface aninal <T>
{
    T getValue(T t);
}
class cat implements aninal {//用默認Object類型
    @Override
    public Object getValue(Object o) {
        return o;
    }
}
class pig implements aninal<String>{//子類不設置泛型,父類接口的泛型需要明確類型
    @Override
    public String getValue(String s) {
        return s+"哼哼";
    }
}
class dog <T>implements aninal<T> {//子類和接口均使用泛型。
    @Override
    public T getValue(T t) {
        return t;
    }
}

也就是說使用泛型接口如果進行繼承依然想使用泛型就需要在繼承的類中事先定義好接口部分的泛型類供接口使用。

class 類 <A,B,C>implements aninal<C> 

泛型方法

泛型函數的基本使用也很容易,和泛型類和泛型接口使用很相似,不過就是菱形需要放到函數類型前面:

public   <T1,T2> void fuc(T1 t1,T2 t2)
{
     System.out.println(t1);
     System.out.println(t2);
 }

邊界限定

泛型既然這麼好用,可以在使用時候直接傳入具體類型,但是我們在開發很多時候某個類或者某個方法的能夠傳入類型是有限制的。那麼在java中有上邊界限定和下邊界限定用來限制泛型的可用類型。
限定通配符包括兩種:

  1. 類型的上界,格式為:<? extends T>,即類型必須為T類型或者T子類
    在這裡插入圖片描述
  2. 類型的下界,格式為:<? super T>,即類型必須為T類型或者T的父類
    在這裡插入圖片描述

舉個例子在Java中編寫這些類和方法:

class  animal {
    //dosomething
}
class dog extends  animal {
    //dosomething
}
class pig extends  animal {
    //dosomething
}
public class test {
    static void printlist1(List<? extends animal>list)//只能aninal和animal的子類
    {
        for(animal animal:list)
        {
            System.out.println(animal);
        }
    }
    static void dolist2(List<? super pig>list)
    {
        //dosomething
    }
}

這樣printlist1函數就使用了上邊界限定。而dolist2函數就用了泛型的下邊界限定,當你錯誤運用時候編譯器就可以提示出來。
在這裡插入圖片描述

尾聲

當然本篇並不是一個完整的泛型解答和總結,泛型還有很多細緻的需要對比其差別這裡就先不介紹啦。

從整體來講,泛型的主要作用還是為了解決類型轉換的安全性問題,避免了Object來回轉換的問題,使得編譯器就能識別類型轉換的錯誤,同時通過限定類型使得函數方法等使用的更加靈活方便。不過泛型更多的應用於框架的編寫方面,在java中其實也是隨處可見。尤其是集合類:

在這裡插入圖片描述
看了這篇泛型,下次設計鏈表二叉樹別傻傻的用int 表示node節點的值了!我想你該知道正確的寫法了!

筆者微信公眾號:bigsai 更多學習資料、精彩內容與你分享!

Leave a Reply

Your email address will not be published. Required fields are marked *