什么是泛型?泛型的基本原理與使用優勢。


1. 什么是泛型?

泛型將接口的概念進一步延伸,“泛型”的字面意思就是廣泛的類型。類、接口和方法代碼可以應用於非常廣泛的類型,代碼與它們能夠操作的數據類型不再綁定在一起,同一套代碼可以用於多種數據類型,這樣不僅可以復用代碼,降低耦合性,而且還提高了代碼的可讀性以及安全性。講起來優點抽象,我們看個實際的例子。

2. 先來看一個簡單的泛型例子

package genericity.demo;

/**
 * @author BastetCat
 * @data 2019/8/8 21:14
 */

public class Pair<T> {
    T one;
    T two;

    public Pair(T one, T two) {
        this.one = one;
        this.two = two;
    }

    public T getOne() {
        return one;
    }

    public T getTwo() {
        return two;
    }
}

觀察和普通類的區別:

  1. 類名后面多了一個<T>
  2. one 和 two的類型都是T

3. 那么T是什么呢?

T 表示類型參數

泛型就是類型參數化,處理的數據類型不是固定的,而是可以作為參數傳入。

現在我們知道了,泛型把類型作為了參數來使用。

4. 如何使用泛型,並將類型作為參數傳入呢?

如下代碼:我們分別new了三Pair對象,其中傳入了不同類型的類型參數(Integer、Character、String)。

package genericity.demo;

/**
 * @author BastetCat
 * @data 2019/8/8 21:22
 */

public class Test {
    public static void main(String[] args) {
        
        Pair<Integer> pairInteger =  new Pair<Integer>(1,2);
        int one1 = pairInteger.getOne();
        int two1 = pairInteger.getTwo();
        System.out.println("one:"+one1+",two:"+two1);

        Pair<Character> pairCharacter  =  new Pair<Character>('一','二');
        char one2 = pairCharacter.getOne();
        char two2 = pairCharacter.getTwo();
        System.out.println("one:"+one2+",two:"+two2);

        Pair<String> pairString  =  new Pair<String>("I","II");
        String one3 = pairString.getOne();
        String two3 = pairString.getTwo();
        System.out.println("one:"+one3+",two:"+two3);
    }
}

結果如下:

one:1,two:2
one:一,two:二
one:I,two:II

當然我們不僅可以傳入一個類型參數,也可以傳入多個類型參數。多個類型參數之間用 逗號“,”隔開。如下面的例子:

package genericity.demo;

/**
 * @author BastetCat
 * @data 2019/8/8 21:37
 */

public class PairTwo <U,V> {
    U one;
    V two;

    public PairTwo(U one, V two) {
        this.one = one;
        this.two = two;
    }

    public U getOne() {
        return one;
    }

    public V getTwo() {
        return two;
    }
}

可以這么使用:

PairTwo<String,Integer> pairTwo = new PairTwo<>("牛牛",20);

注意:自 Java 7 開始,支持省略后面的類型參數,讓書寫更簡單些。

5. 泛型的基本原理

泛型類型參數到底是什么?為什么一定要定義類型參數呢?定義普通類,直接使用Object也是可以呀。如之前的Pair類我們可以寫成:

package genericity.demo;

/**
 * @author BastetCat
 * @data 2019/8/8 21:44
 */

public class PairObject {
    Object one;
    Object two;

    public PairObject(Object one, Object two) {
        this.one = one;
        this.two = two;
    }

    public Object getOne() {
        return one;
    }

    public Object getTwo() {
        return two;
    }
}

然后這樣使用,也是同樣的效果:

package genericity.demo;

/**
 * @author BastetCat
 * @data 2019/8/8 21:46
 */

public class TestPairObject {
    public static void main(String[] args) {
        PairObject pairObject1 = new PairObject(1,2);
        int one1 =(int)pairObject1.getOne();
        int two1 =(int)pairObject1.getTwo();
        System.out.println("one:"+one1+",two:"+two1);

        PairObject pairObject2 = new PairObject("yi","er");
        String one2 =(String)pairObject2.getOne();
        String two2 =(String)pairObject2.getTwo();
        System.out.println("one:"+one2+",two:"+two2);
    }
}

輸出結果:

one:1,two:2
one:yi,two:er

我們可以看到,確確實實我們的使用Object + 強制類型轉換也實現了相同的結果。事實上,我們Java泛型的內部原理就是這樣的。

我們使用JAD工具來反編譯我們的Pair.class 與 Test.class得到的結果如下:

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) 
// Source File Name:   Pair.java

package genericity.demo;
public class Pair
{
    public Pair(Object obj, Object obj1)
    {
        one = obj;
        two = obj1;
    }
    public Object getOne()
    {
        return one;
    }
    public Object getTwo()
    {
        return two;
    }
    Object one;
    Object two;
}




// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) 
// Source File Name:   Test.java

package genericity.demo;
import java.io.PrintStream;
// Referenced classes of package genericity.demo:
//            Pair

public class Test
{
    public Test()
    {
    }
    public static void main(String args[])
    {
        Pair pair = new Pair(Integer.valueOf(1), Integer.valueOf(2));
        int i = ((Integer)pair.getOne()).intValue();
        int j = ((Integer)pair.getTwo()).intValue();
        System.out.println((new StringBuilder()).append("one:").append(i).append(",two:").append(j).toString());
        Pair pair1 = new Pair(Character.valueOf('\u4E00'), Character.valueOf('\u4E8C'));
        char c = ((Character)pair1.getOne()).charValue();
        char c1 = ((Character)pair1.getTwo()).charValue();
        System.out.println((new StringBuilder()).append("one:").append(c).append(",two:").append(c1).toString());
        Pair pair2 = new Pair("I", "II");
        String s = (String)pair2.getOne();
        String s1 = (String)pair2.getTwo();
        System.out.println((new StringBuilder()).append("one:").append(s).append(",two:").append(s1).toString());
    }
}

通過以上的分析:

我們可以得知,Java的編譯器將java源文件編譯成字節碼.class文件,虛擬機加載並運行。對於泛型類,java編譯器會將其轉換為普通的非泛型代碼。將類型T擦除,然后替換為Object,插入必要的強制類型轉換。Java虛擬機實際執行的時候,並不知道泛型這回事,只知道普通的類及代碼。

那么為什么泛型要這樣設計呢?

因為泛型是 Java 5 以后才支持的,這么設計是為了兼容性,而不得已的一個選擇。

6. 使用泛型的好處

  • 代碼復用:我們一套代碼可以支持不同的類性。
  • 降低了耦合性:代碼邏輯和數據類型之間分離,實現了解耦。
  • 更好的可讀性:我們在使用集合的時候,定義了一個list 如List<String>,一看便知道這個一個存放String類型的list。
  • 更高的安全性:語言和程序設計的一個重要目標就是將bug消滅在搖籃里,能在寫的時候消滅,就不要留在運行的時候。如我們定義一個List<String>這樣的一個list。當我們往list里面放其他非String類型的數據時,我們的IDE(如Eclipse)就會報錯提示。就算沒有IDE。編譯時,Java編譯器也會提示,這稱之為類型安全。這樣就為程序設置了一道安全防護。同樣的,使用泛型還可以省去使用普通對象時繁瑣的強制類型轉換。相反,使用普通對象,編譯時並不會提示。假如傳入的參數類型和最后強制類型轉換的類型不一致。運行時就會出現ClassCastException,使用泛型則不會。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM