Java 基礎(一)| 使用泛型的正確姿式

泛型

前言

爲跳槽面試作準備,今天開始進入 Java 基礎的複習。但願基礎很差的同窗看完這篇文章,能掌握泛型,而基礎好的同窗權當複習,但願看完這篇文章可以起一點你的青澀記憶。html

1、什麼是泛型

泛型,即「參數化類型」。一提到參數,最熟悉的就是定義方法時有形參,而後調用此方法時傳遞實參。那麼參數化類型怎麼理解呢?前端

顧名思義,就是將類型由原來的具體的類型參數化(動詞),相似於方法中的變量參數,此時類型也定義成參數形式(能夠稱之爲類型形參),java

而後在使用/調用時傳入具體的類型(類型實參)。python

泛型的本質是爲了參數化類型(在不建立新的類型的狀況下,經過泛型指定的不一樣類型來控制形參具體限制的類型)。也就是說在泛型使用過程當中。linux

操做的數據類型被指定爲一個參數,這種參數類型能夠用在類、接口和方法中,分別被稱爲泛型類、泛型接口、泛型方法。c++

參考:www.cnblogs.com/coprince/p/…git

1.1常見的泛型類型變量:

E:元素(Element),多用於 java 集合框架 K:關鍵字(Key) N:數字(Number) T:類型(Type) V:值(Value)github

2、爲何要使用泛型

回答這個問題前,首先舉兩個栗子,我想打印字符串到控制檯,以下代碼:面試

package com.nasus.generic;

import java.util.ArrayList;
import java.util.List;

/** * Project Name:review_java <br/> * Package Name:com.nasus.generic <br/> * Date:2019/12/28 20:58 <br/> * * @author <a href="turodog@foxmail.com">chenzy</a><br/> */
public class Show {

    public static void main(String[] args) {
        List list=new ArrayList();
        list.add("一個優秀的廢人");
        list.add("java 工程師");
        list.add(666);
        for (int i = 0; i < list.size(); i++) {
            String value= (String) list.get(i);
            System.out.println(value);
        }
    }
}
複製代碼

自己個人 list 是打算裝載 String 去打印的,可是你們發現沒有?我傳入 int 型時(編譯期),Java 是沒有任何提醒的(頂可能是 IDEA 警告)。直到我循環調用(運行期)打印方法,打印 int 型時,Java 才報錯:算法

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
一個優秀的廢人
	at com.nasus.generic.Show.main(Show.java:23)
java 工程師
複製代碼

第二栗子,我想實現一個能夠操做各類類型的加法,以下代碼:

package com.nasus.generic.why;

/** * Project Name:review_java <br/> * Package Name:com.nasus.generic <br/> * Date:2019/12/28 21:18 <br/> * * @author <a href="turodog@foxmail.com">chenzy</a><br/> */
public class Add {

    private static int add(int a, int b) {
        System.out.println(a + "+" + b + "=" + (a + b));
        return a + b;
    }

    private static float add(float a, float b) {
        System.out.println(a + "+" + b + "=" + (a + b));
        return a + b;
    }

    private static double add(double a, double b) {
        System.out.println(a + "+" + b + "=" + (a + b));
        return a + b;
    }

    // 一個泛型方法
    private static <T extends Number> double add(T a, T b) {
        System.out.println(a + "+" + b + "=" + (a.doubleValue() + b.doubleValue()));
        return a.doubleValue() + b.doubleValue();
    }

    public static void main(String[] args) {
        Add.add(1, 2);
        Add.add(1f, 2f);
        Add.add(1d, 2d);
        System.out.println("--------------------------");
        // 如下三個都是調用泛型方法
        Add.add(Integer.valueOf(1), Integer.valueOf(2));
        Add.add(Float.valueOf(1), Float.valueOf(2));
        Add.add(Double.valueOf(1), Double.valueOf(2));
    }
}
複製代碼

這個加法能夠操做 int、float、double 類型,但相應的也必須重寫對應的加法,而此時我其實能夠就用一個泛型方法就實現了上面三個重載方法的功能。

1+2=3
1.0+2.0=3.0
1.0+2.0=3.0
--------------------------
1+2=3.0
1.0+2.0=3.0
1.0+2.0=3.0
複製代碼

因此使用泛型緣由有三個:

  • 提升可讀性
  • 使 ClassCastException 這種錯誤在編譯期就檢測出來
  • 適用於多種數據類型執行相同的代碼(代碼複用)

參考:www.jianshu.com/p/986f732ed…

3、泛型詳解

3.1泛型類

由咱們指定想要傳入泛型類中的類型,把泛型定義在類上,用戶使用該類的時候,才把類型明確下來,好比:定義一個萬能的實體數據暫存工具類。

注意:泛型類在初始化時就把類型肯定了

package com.nasus.generic.how;

/** * Project Name:review_java <br/> * Package Name:com.nasus.generic.how <br/> * Date:2019/12/28 21:35 <br/> * * @author <a href="turodog@foxmail.com">chenzy</a><br/> */
public class EntityTool<T> {

    private T entity;

    public T getEntity() {
        return entity;
    }

    public void setEntity(T entity) {
        this.entity = entity;
    }

    public static void main(String[] args) {
        // 建立對象並指定元素類型
        EntityTool<String> stringTool = new EntityTool<>();
        stringTool.setEntity("一個優秀的廢人");
        String s = stringTool.getEntity();
        System.out.println(s);


        // 建立對象並指定元素類型
        EntityTool<Integer> integerTool = new EntityTool<>();
        // 此時,若是這裏傳入的仍是 String 類型,那就會在編譯期報錯
        integerTool.setEntity(10);
        int i = integerTool.getEntity();
        System.out.println(i);
    }
}
複製代碼

3.2泛型方法

有時候咱們只想在方法中使用泛型,能夠這麼定義:

值得注意的是:

  • 與泛型類不一樣,泛型方法在調用時才肯定最終類型
  • 如有返回值,返回值不須要強轉
package com.nasus.generic.how;

/** * Project Name:review_java <br/> * Package Name:com.nasus.generic.how <br/> * Date:2019/12/28 21:46 <br/> * * @author <a href="turodog@foxmail.com">chenzy</a><br/> */
public class Show {

    public static  <T> T show(T t) {
        System.out.println(t);
        return t;
    }

    public static void main(String[] args) {
        // 返回值不用強轉,傳進去是什麼,返回就是什麼
        String s = show("一個優秀的廢人");
        int num1 = show(666);
        double num2 = show(666.666);
        System.out.println("------------------------");
        System.out.println(s);
        System.out.println(num1);
        System.out.println(num2);
    }
}
複製代碼

3.3泛型接口

泛型接口分兩種實現方法:

一是實現類不明確泛型接口的類型參數變量,這時實現類也必須定義類型參數變量(好比下面 Showimpl)

接口:

public interface Show<T> {
    void show(T t);
}
複製代碼
public class ShowImpl<T> implements Show<T>{

    @Override
    public void show(T t) {
        System.out.println(t);
    }

    public static void main(String[] args) {
        ShowImpl<String> stringShow = new ShowImpl<>();
        stringShow.show("一個優秀的廢人");
    }
}
複製代碼

二是明確泛型接口的類型參數變量

public class ShowImpl2 implements Show<String>{

    @Override
    public void show(String s) {
        System.out.println("一個優秀的廢人");
    }
}
複製代碼

3.5 限定泛型類型變量

限定泛型類型上限

其實就是至關於指定了泛型類的父類 聲明類:類名<泛型標識 extends 類>{}

在類中使用:

// 用在類上
public class Show<T extends Number> {

    private T show(T t){
        System.out.println(t);
        return t;
    }

    public static void main(String[] args) {
        // 初始化時指定類型
        Show<Integer> show = new Show<>();
        show.show(6666666);

        // 報錯,該類只接受繼承於 Number 的泛型參數
        // Show<String> stringShow = new Show<>();
    }
}
複製代碼

方法中使用:

定義對象:類名<泛型標識 extends 類> 對象名稱

public class Info<T> {

    // 定義泛型變量
    private T var;

    public void setVar(T var) {
        this.var = var;
    }

    public T getVar() {
        return this.var;
    }

    public String toString() {
        return this.var.toString();
    }
}
複製代碼
public class ShowInfo {

    // 用在方法上,只能接收 Number 及其子類
    public static void showInfo(Info<? extends Number> t) {
        System.out.print(t);
    }

    public static void main(String args[]) {
        Info<Integer> i1 = new Info<>();
        Info<Float> i2 = new Info<>();
        i1.setVar(666666666);
        i2.setVar(666666.66f);
        showInfo(i1);
        showInfo(i2);
    }

}
複製代碼

限定泛型類型下限

定義對象:類名<泛型標識 extends 類> 對象名稱

與指定上限相反,指定下限定很簡單,就是至關於指定了泛型類的子類,再也不贅述。

public class ShowInfo {

    // 只接受 String 的父類
    public static void showInfo(Info<? super String> t) {
        System.out.println(t);
    }

    public static void main(String args[]) {
        Info<String> stringInfo = new Info<>();
        Info<Object> objectInfo = new Info<>();
        stringInfo.setVar("一個優秀的廢人");
        objectInfo.setVar(new Object());
        showInfo(stringInfo);
        showInfo(objectInfo);
    }

}
複製代碼

3.6 通配符類型

  • <? extends Parent> 指定了泛型類型的上限
  • <? super Child> 指定了泛型類型的下屆
  • <?> 指定了沒有限制的泛型類型

3.7 泛型擦除

泛型是提供給 javac 編譯器使用的,它用於限定集合的輸入類型,讓編譯器在源代碼級別上,即擋住向集合中插入非法數據。但編譯器編譯完帶有泛形的 java 程序後,生成的 class 文件中將再也不帶有泛形信息,以此使程序運行效率不受到影響,這個過程稱之爲 「擦除」。

3.8 泛型的使用規範

一、不能實例化泛型類 二、靜態變量或方法不能引用泛型類型變量,可是靜態泛型方法是能夠的 三、基本類型沒法做爲泛型類型 四、沒法使用 instanceof 關鍵字或 == 判斷泛型類的類型 五、泛型類的原生類型與所傳遞的泛型無關,不管傳遞什麼類型,原生類是同樣的 六、泛型數組能夠聲明但沒法實例化 七、泛型類不能繼承 Exception 或者 Throwable 八、不能捕獲泛型類型限定的異常但能夠將泛型限定的異常拋出

最後

若是看到這裏,喜歡這篇文章的話,幫忙 " 轉發 "或者點個" 在看 ",行嗎?祝大家 2020 暴富。微信搜索「一個優秀的廢人」,歡迎關注。

回覆「1024」送你一套完整的 java、python、c++、go、前端、linux、算法、大數據、人工智能、小程序以及英語教程。

回覆「電子書」送你 50+ 本 java 電子書。

最全教程
相關文章
相關標籤/搜索