Java 基礎(一)


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

前言

今天開始進入 Java 基礎的複習。希望基礎不好的同學看完這篇文章,能掌握泛型,而基礎好的同學權當複習,希望看完這篇文章能夠起你更高昂的攻城速度。

一、什麼是泛型

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

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

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

泛型的本質是為了參數化類型(在不創建新的類型的情況下,通過泛型指定的不同類型來控制形參具體限制的類型)。也就是說在泛型使用過程中。

操作的數據類型被指定為一個參數,這種參數類型可以用在類、接口和方法中,分別被稱為泛型類、泛型接口、泛型方法。

1.1常見的泛型類型變量:

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

二、為什麼要使用泛型

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

<code>package com.nasus.generic;import java.util.ArrayList;import java.util.List;/** * Project Name:review_java 
* Package Name:com.nasus.generic
* Date:2019/12/28 20:58
* * @author
*/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); } }}/<code>

本身我的 list 是打算裝載 String 去打印的,但是大家發現沒有?我傳入 int 型時(編譯期),Java 是沒有任何提醒的(頂多是 IDEA 警告)。直到我循環調用(運行期)打印方法,打印 int 型時,Java 才報錯:

<code>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 工程師/<code>

第二栗子,我想實現一個可以操作各種類型的加法,如下代碼:

<code>package com.nasus.generic.why;/** * Project Name:review_java 
* Package Name:com.nasus.generic
* Date:2019/12/28 21:18
* * @author

*/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 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)); }}/<code>

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

<code>1+2=31.0+2.0=3.01.0+2.0=3.0--------------------------1+2=3.01.0+2.0=3.01.0+2.0=3.0/<code>

所以使用泛型原因有三個:

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

參考:https://www.jianshu.com/p/986f732ed2f1

三、泛型詳解

3.1泛型類

由我們指定想要傳入泛型類中的類型,把泛型定義在類上,用戶使用該類的時候,才把類型明確下來

,比如:定義一個萬能的實體數據暫存工具類。

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

<code>package com.nasus.generic.how;/** * Project Name:review_java 
* Package Name:com.nasus.generic.how
* Date:2019/12/28 21:35
* * @author
*/public class EntityTool { 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); }}/<integer>/<string>/<code>

3.2泛型方法

有時候我們只想在方法中使用泛型,可以這麼定義:

值得注意的是:

  • 與泛型類不同,泛型方法在調用時才確定最終類型
  • 若有返回值,返回值不需要強轉
<code>package com.nasus.generic.how;/** * Project Name:review_java 
* Package Name:com.nasus.generic.how
* Date:2019/12/28 21:46
* * @author

*/public class Show { public static 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); }}/<code>

3.3泛型接口

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

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

接口:

<code>public interface Show {    void show(T t);}/<code>
<code>public class ShowImpl implements Show{    @Override    public void show(T t) {        System.out.println(t);    }    public static void main(String[] args) {        ShowImpl<string> stringShow = new ShowImpl<>();        stringShow.show("一個優秀的廢人");    }}/<string>/<code>

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

<code>public class ShowImpl2 implements Show<string>{    @Override    public void show(String s) {        System.out.println("一個優秀的廢人");    }}/<string>/<code>

3.5 限定泛型類型變量

限定泛型類型上限

其實就是相當於指定了泛型類的父類

聲明類:類名{}

在類中使用:

<code>// 用在類上public class Show {    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<>();    }}/<string>/<integer>/<code>

方法中使用:

定義對象:類名 對象名稱

<code>public class Info {    // 定義泛型變量    private T var;    public void setVar(T var) {        this.var = var;    }    public T getVar() {        return this.var;    }    public String toString() {        return this.var.toString();    }}/<code>
<code>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);    }}/<float>/<integer>/<code>

限定泛型類型下限

定義對象:類名 對象名稱

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

<code>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);    }}複製代碼/<object>/<string>/<code>

3.6 通配符類型

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

3.7 泛型擦除

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

3.8 泛型的使用規範

1、不能實例化泛型類 2、靜態變量或方法不能引用泛型類型變量,但是靜態泛型方法是可以的 3、基本類型無法作為泛型類型 4、無法使用 instanceof 關鍵字或 == 判斷泛型類的類型 5、泛型類的原生類型與所傳遞的泛型無關,無論傳遞什麼類型,原生類是一樣的 6、泛型數組可以聲明但無法實例化 7、泛型類不能繼承 Exception 或者 Throwable 8、不能捕獲泛型類型限定的異常但可以將泛型限定的異常拋出

如果小編的分享對客觀有所幫助,那就點個關注,小編將持續為你推出更多的IT知識分享。


分享到:


相關文章: