欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

半小時(shí)通透Java的泛型

 更新時(shí)間:2021年09月03日 15:33:31   作者:小蝸牛耶  
這篇文章主要給大家介紹了關(guān)于Java中泛型使用的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

Java 泛型

前言

編程不能停止,每天發(fā)一篇~

不要捉急往后學(xué),前面基礎(chǔ)打的越牢固后面進(jìn)階越好進(jìn)階

基礎(chǔ)不牢,地動(dòng)山搖

學(xué)習(xí)目標(biāo)

什么是泛型

為什么需要泛型

如何使用泛型

如何自定義泛型

類(lèi)型通配符等知識(shí)

1. 什么是泛型

泛型不只是 Java 語(yǔ)言所特有的特性,泛型是程序設(shè)計(jì)語(yǔ)言的一種特性。

允許程序員在強(qiáng)類(lèi)型的程序設(shè)計(jì)語(yǔ)言中編寫(xiě)代碼時(shí)定義一些可變部分,那些部分在使用前必須做出聲明。

Java 中的集合類(lèi)是支持泛型的,它在代碼中是這個(gè)樣子的

請(qǐng)?zhí)砑訄D片描述

代碼中的<Integer>就是泛型,我們把類(lèi)型像參數(shù)一樣傳遞,尖括號(hào)中間就是數(shù)據(jù)類(lèi)型,我們可以稱(chēng)之為實(shí)際類(lèi)型參數(shù),這里實(shí)際類(lèi)型參數(shù)的數(shù)據(jù)類(lèi)型只能為引用數(shù)據(jù)類(lèi)型。

那么為什么需要泛型呢?

2. 為什么需要泛型

我們?cè)谑褂?code>ArrayList實(shí)現(xiàn)類(lèi)的時(shí)候,如果沒(méi)有指定泛型,IDEA會(huì)給出警告,代碼似乎也是可以順利運(yùn)行的。請(qǐng)看如下實(shí)例:

import java.util.ArrayList;

public class testDemo1 {

    public static void main(String[] args) {
        ArrayList arrayList = new ArrayList();
        arrayList.add("Hello");
        String str1 = (String) arrayList.get(0);
        System.out.println("str=" + str1);
    }

}

運(yùn)行結(jié)果:

str1=Hello

雖然運(yùn)行時(shí)沒(méi)有發(fā)生任何異常,但這樣做有兩個(gè)缺點(diǎn):

  1. 需要強(qiáng)制類(lèi)型轉(zhuǎn)換: 由于ArrayList內(nèi)部就是一個(gè)Object[]數(shù)組,在get()元素的時(shí)候,返回的是Object類(lèi)型,所以在ArrayList外獲取該對(duì)象,需要強(qiáng)制類(lèi)型轉(zhuǎn)換。其它的CollectionMap如果不使用泛型,也存在這個(gè)問(wèn)題;
  2. 可向集合中添加任意類(lèi)型的對(duì)象,存在類(lèi)型不安全風(fēng)險(xiǎn)。例如如下代碼中,我們向列表中既添加了Integer類(lèi)型,又添加了String類(lèi)型:
package com.caq.oop.demo08;

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

public class Test {
    public static void main(String[] args) {
        //實(shí)例化一個(gè)空列表
        List arrayList = new ArrayList<>();
        arrayList.add(123);
        arrayList.add("sad");
        String str = (String) arrayList.get(0);

    }
}

Exception in thread “main” java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at com.caq.oop.demo08.Test.main(Test.java:12)

由于我們的“疏忽”,列表第 1 個(gè)元素實(shí)際上是整型,但被我們強(qiáng)制轉(zhuǎn)換為字符串類(lèi)型,這是行不通的,因此會(huì)拋出ClassCastException異常。

使用泛型可以解決這些問(wèn)題。泛型有如下優(yōu)點(diǎn):

  1. 可以減少類(lèi)型轉(zhuǎn)換的次數(shù),代碼更加簡(jiǎn)潔;
  2. 程序更加健壯:只要編譯期沒(méi)有警告,運(yùn)行期就不會(huì)拋出ClassCastException異常;
  3. 提高了代碼的可讀性:編寫(xiě)集合的時(shí)候,就限定了集合中能存放的類(lèi)型。

3. 如何使用泛型

3.1 泛型使用

在代碼中,這樣使用泛型:

List<String> list = new ArrayList<String>();
// Java 7 及以后的版本中,構(gòu)造方法中可以省略泛型類(lèi)型:
List<String> list = new ArrayList<>();
外幣巴伯
    

要注意的是,變量聲明的類(lèi)型必須與傳遞給實(shí)際對(duì)象的類(lèi)型保持一致,下面是錯(cuò)誤的例子:

List<Object> list = new ArrayList<String>();
List<Number> numbers = new ArrayList(Integer);

3.2 自定義泛型類(lèi)

3.2.1 Java 源碼中泛型的定義

在自定義泛型類(lèi)之前,我們來(lái)看下java.util.ArrayList是如何定義的:

img

類(lèi)名后面的<E>就是泛型的定義,E不是 Java 中的一個(gè)具體的類(lèi)型,它是 Java 泛型的通配符(注意是大寫(xiě)的,實(shí)際上就是Element的含義),可將其理解為一個(gè)占位符,將其定義在類(lèi)上,使用時(shí)才確定類(lèi)型。

此處的命名不受限制,但最好有一定含義,例如java.lang.HashMap的泛型定義為HashMap<K,V>,K表示Key,V表示Value。

3.2.2 自定義泛型類(lèi)實(shí)例1

下面我們來(lái)自定義一個(gè)泛型類(lèi),自定義泛型按照約定俗成可以叫<T>,具有Type的含義,實(shí)例如下:

實(shí)例演示

package com.caq.List;

public class Generic01<T> {

    private T abc;//定義在類(lèi)上的泛型,在類(lèi)內(nèi)部可以使用

    public T getAbc() {
        return abc;
    }

    public void setAbc(T abc) {
        this.abc = abc;
    }

    public static void main(String[] args) {
        //實(shí)例化對(duì)象,指定元素類(lèi)型為整型
        Generic01<Integer> integerGeneric01 = new Generic01<>();
        //調(diào)用方法
        integerGeneric01.setAbc(100);
        System.out.println("integerGeneric01="+ integerGeneric01.getAbc());

        //實(shí)例化對(duì)象,指定元素類(lèi)型為長(zhǎng)類(lèi)型
        Generic01<Long> longGeneric01 = new Generic01<>();
        longGeneric01.setAbc(200L);
        System.out.println("longGeneric01="+ longGeneric01.getAbc());

        // 實(shí)例化對(duì)象,指定元素類(lèi)型為雙精度浮點(diǎn)型
        Generic01<Double> doubleGeneric01 = new Generic01<>();
        doubleGeneric01.setAbc(300.0);
        System.out.println("doubleGeneric01="+ doubleGeneric01.getAbc());
        
    }
}

運(yùn)行結(jié)果:

integerGeneric01=100
longGeneric01=200
doubleGeneric01=300.0

我們?cè)陬?lèi)的定義處也定義了泛型:Generic01<T>;在類(lèi)內(nèi)部定義了一個(gè)T類(lèi)型的abc變量,并且為其添加了settergetter方法。

解釋?zhuān)簩?duì)于泛型類(lèi)的使用也很簡(jiǎn)單,在主方法中,創(chuàng)建對(duì)象的時(shí)候指定T的類(lèi)型分別為IntegerLong、Double,類(lèi)就可以自動(dòng)轉(zhuǎn)換成對(duì)應(yīng)的類(lèi)型了。

3.2.3 自定義泛型類(lèi)實(shí)例2

上面我們知道了如何定義含有單個(gè)泛型的類(lèi),那么對(duì)于含有多個(gè)泛型的類(lèi),如何定義呢?

我們可以看一下HashMap類(lèi)是如何定義的。如下是 Java 源碼的截圖:

img

參照HashMap<K,V>類(lèi)的定義,下面我們來(lái)看看如何定義含有兩個(gè)泛型的類(lèi)

package com.caq.List;

public class Generic02<K, V> {//這次是定義兩個(gè)泛型在類(lèi)上

    //定義類(lèi)型為K的key屬型
    private K key;

    //定義類(lèi)型為V的value屬型
    private V value;

    //封裝里的知識(shí),通過(guò)Getter和Setter方法來(lái)設(shè)置和獲取私有屬型的值
    public K getKey() {
        return key;
    }

    public void setKey(K key) {
        this.key = key;
    }

    public V getValue() {
        return value;
    }

    public void setValue(V value) {
        this.value = value;
    }

    public static void main(String[] args) {
        //實(shí)例化對(duì)象,分別指定類(lèi)型為整型,長(zhǎng)整型
        Generic02<Integer, Long> integerLongGeneric02 = new Generic02<>();
        //實(shí)例化對(duì)象,分別指定類(lèi)型為浮點(diǎn)型、字符串類(lèi)型
        Generic02<Float, String> floatStringGeneric02 = new Generic02<>();

        integerLongGeneric02.setKey(100);
        integerLongGeneric02.setValue(200L);
        System.out.println("key=" + integerLongGeneric02.getKey());
        System.out.println("value=" + integerLongGeneric02.getValue());

        floatStringGeneric02.setKey(0.9f);
        floatStringGeneric02.setValue("巴啦啦能量");
        System.out.println("key=" + floatStringGeneric02.getKey());
        System.out.println("value=" + floatStringGeneric02.getValue());
    }
}

運(yùn)行結(jié)果:

key=100value=200key=0.9value=巴啦啦能量

3.3 自定義泛型方法

前面我們知道了如何定義泛型類(lèi),在類(lèi)上定義的泛型,在方法中也可以使用。下面我們來(lái)看一下如何自定義泛型方法。

泛型方法不一定寫(xiě)在泛型類(lèi)當(dāng)中。當(dāng)類(lèi)的調(diào)用者總是關(guān)心類(lèi)中的某個(gè)泛型方法,不關(guān)心其他屬性,這個(gè)時(shí)候就沒(méi)必要再整個(gè)類(lèi)上定義泛型了。

直接在方法上設(shè)置泛型(generic)

package com.caq.List;public class Generic03 {        public <T> void test(T t){        System.out.println(t);    }    public static void main(String[] args) {        Generic03 generic03 = new Generic03();                generic03.test("Monkey");        generic03.test(1);        generic03.test(1.00000);        generic03.test(1L);    }}

運(yùn)行結(jié)果:

Monkey11.01

實(shí)例中,使用<T>來(lái)定義test方法的泛型,它接收一個(gè)泛型的參數(shù)變量并在方法體打??;調(diào)用泛型方法也很簡(jiǎn)單,在主方法中實(shí)例化對(duì)象,調(diào)用對(duì)象下的泛型方法,可傳入不同類(lèi)型的參數(shù)。

4. 泛型類(lèi)的子類(lèi)

泛型類(lèi)也是一個(gè) Java 類(lèi),它也具有繼承的特性。

泛型類(lèi)的繼承可分為兩種情況:

  1. 子類(lèi)明確泛型類(lèi)的類(lèi)型參數(shù)變量;
  2. 子類(lèi)不明確泛型類(lèi)的類(lèi)型參數(shù)變量。

4.1 明確類(lèi)型參數(shù)變量

例如,有一個(gè)泛型接口:

package com.caq.List;public interface GenericInterface01<T> {    default void show(T t) {            }}

泛型接口的實(shí)現(xiàn)類(lèi)如下:

package com.caq.List;public class GenericInterfaceImple implements GenericInterface01<String> {    @Override    public void show(String s) {        System.out.println(s);    }}

子類(lèi)實(shí)現(xiàn)明確了泛型的參數(shù)變量為String類(lèi)型。因此方法show()的重寫(xiě)也將T替換為了String類(lèi)型。

4.2 不明確類(lèi)型參數(shù)變量

當(dāng)實(shí)現(xiàn)類(lèi)不確定泛型類(lèi)的參數(shù)變量時(shí),實(shí)現(xiàn)類(lèi)需要定義類(lèi)型參數(shù)變量,調(diào)用者使用子類(lèi)時(shí),也需要傳遞類(lèi)型參數(shù)變量。

如下是GenericInterface接口的另一個(gè)實(shí)現(xiàn)類(lèi):

package com.caq.List;

public class GenericInterfaceImple<T> implements GenericInterface01<T> {
    @Override
    public void show(T t) {
        System.out.println(t);
    }
}

在主方法中調(diào)用實(shí)現(xiàn)類(lèi)的show()方法:

package com.caq.List;

public class GenericInterfaceImple<T> implements GenericInterface01<T> {
    @Override
    public void show(T t) {
        System.out.println(t);
    }

    public static void main(String[] args) {
        GenericInterfaceImple<Integer> integerGenericInterfaceImple = new GenericInterfaceImple<>();
        integerGenericInterfaceImple.show(100);
    }
}


100

5. 類(lèi)型通配符

我們先來(lái)看一個(gè)泛型作為方法參數(shù)的實(shí)例:

package com.caq.List;
/**
 * 遍歷并打印集合中的每一個(gè)元素
 * 遍歷是二叉樹(shù)上最重要的運(yùn)算之一,是二叉樹(shù)上進(jìn)行其它運(yùn)算之基礎(chǔ)。 樹(shù)的遍歷是樹(shù)的一種重要的運(yùn)算。 
 * 所謂遍歷是指對(duì)樹(shù)中所有結(jié)點(diǎn)的信息的訪問(wèn),即依次對(duì)樹(shù)中每個(gè)結(jié)點(diǎn)訪問(wèn)一次且僅訪問(wèn)一次。

 * @param list 要接收的集合
 */
public class Generic04 {
    public void printListElement(List<object> list){
        for (Object o :
                list) {
            System.out.println(o);
        }
    }
}

觀察上面的代碼,參數(shù)list的限定的泛型類(lèi)型為Object, 也就是說(shuō),這個(gè)方法只能接收元素為Object類(lèi)型的集合,如果我們想傳遞其他元素類(lèi)型的集合,是行不通的。例如,如果傳遞裝載Integer元素的集合,程序在編譯階段就會(huì)報(bào)錯(cuò):

請(qǐng)?zhí)砑訄D片描述

Tips: 泛型中的List<Object>并不是List<Integer>的父類(lèi),它們不滿(mǎn)足繼承關(guān)系。

5.1 無(wú)限定通配符

想要解決這個(gè)問(wèn)題,使用類(lèi)型通配符即可,修改方法參數(shù)處的代碼,將<>中間的Object改為?即可:

    public void printListElement(List<?> list){

此處的?就是類(lèi)型通配符,表示可以匹配任意類(lèi)型,因此調(diào)用方可以傳遞任意泛型類(lèi)型的列表。

實(shí)例演示

package com.caq.List;

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


public class Generic04 {
//List<?>可以理解為列表的類(lèi)型,可以是整數(shù)型列表,也可以是字符串類(lèi)型列表,list代表的是遍歷的元素代表
    public void printListElement(List<?> list){
        for (Object o : list) {
            System.out.println(o);
        }
    }

    public static void main(String[] args) {
        //實(shí)例化一個(gè)整型列表
        List<Integer> interger = new ArrayList<>();
        //加元素
        interger.add(1);
        interger.add(2);
        interger.add(2222);

        //實(shí)例化對(duì)象
        Generic04 generic04 = new Generic04();
        generic04.printListElement(interger);

        //實(shí)例化一個(gè)字符串類(lèi)型列表
        ArrayList<String> strings = new ArrayList<>();
        strings.add("element1");
        strings.add("element2");
        strings.add("element3");

        generic04.printListElement(strings);
    }
}

運(yùn)行結(jié)果:

1
2
2222
element1
element2
element3

5.2 extends 通配符

extends通配符用來(lái)限定泛型的上限。什么意思呢?依舊以上面的實(shí)例為例,我們來(lái)看一個(gè)新的需求,我們希望方法接收的List 集合限定在數(shù)值類(lèi)型內(nèi)(float、integer、double、byte 等),不希望其他類(lèi)型可以傳入(比如字符串)。此時(shí),可以改寫(xiě)上面的方法定義,設(shè)定上界通配符:

public void printListElement(List<? extends Number> list) {

這樣的寫(xiě)法的含義為:List集合裝載的元素只能是Number自身或其子類(lèi)(Number類(lèi)型是所有數(shù)值類(lèi)型的父類(lèi)),完整實(shí)例如下:

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

public class Generic04 {

    public void printListElement(List<? extends Number> list) {
        for (Object o : list) {
            System.out.println(o);
        }
    }

    public static void main(String[] args) {
        // 實(shí)例化一個(gè)整型的列表
        List<Integer> integers = new ArrayList<>();
        // 添加元素
        integers.add(1);
        integers.add(2);
        integers.add(3);
        GenericDemo4 generic04 = new Generic04();
        // 調(diào)用printListElement()方法
        generic04.printListElement(integers);

    }
}

運(yùn)行結(jié)果:

1
2
3

5.3 super 通配符

既然已經(jīng)了解了如何設(shè)定通配符上界,也就不難理解通配符的下界了,可以限定傳遞的參數(shù)只能是某個(gè)類(lèi)型的父類(lèi)。

語(yǔ)法如下:

<? super Type>

6. 小結(jié)

  • 使用泛型可以避免強(qiáng)制類(lèi)型轉(zhuǎn)換,也可以避免運(yùn)行期就拋出的ClassCastException異常
  • 在使用泛型時(shí),要注意變量聲明的泛型類(lèi)型要匹配傳遞給實(shí)際對(duì)象的類(lèi)型, Java 7 及以后的版本中,構(gòu)造方法中可以省略泛型類(lèi)型,推薦直接省略
  • 如何自定義泛型類(lèi)和泛型方法,在實(shí)際的開(kāi)發(fā)中,我們想要編寫(xiě)比較通用的代碼就避免不了使用泛型,慢慢體會(huì)星弟們~~~
  • 另外,泛型也是可以繼承的
  • 類(lèi)型通配符的概念和使用場(chǎng)景

到此這篇關(guān)于半小時(shí)通透Java的泛型的文章就介紹到這了,更多相關(guān)Java泛型內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Feign 使用HttpClient和OkHttp方式

    Feign 使用HttpClient和OkHttp方式

    這篇文章主要介紹了Feign 使用HttpClient和OkHttp方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • java編程實(shí)現(xiàn)求質(zhì)數(shù)與因式分解代碼分享

    java編程實(shí)現(xiàn)求質(zhì)數(shù)與因式分解代碼分享

    這篇文章主要介紹了Java編程實(shí)現(xiàn)求質(zhì)數(shù)與因式分解代碼分享,對(duì)二者的概念作了簡(jiǎn)單介紹(多此一舉,哈哈),都是小學(xué)數(shù)學(xué)老師的任務(wù),然后分享了求解質(zhì)數(shù)和因式分解的Java代碼,具有一定借鑒價(jià)值,需要的朋友可以參考下。
    2017-12-12
  • Java實(shí)現(xiàn)商品的查找、添加、出庫(kù)、入庫(kù)操作完整案例

    Java實(shí)現(xiàn)商品的查找、添加、出庫(kù)、入庫(kù)操作完整案例

    這篇文章主要介紹了Java實(shí)現(xiàn)商品的查找、添加、出庫(kù)、入庫(kù)操作,結(jié)合完整實(shí)例形式分析了java基于面向?qū)ο蟮纳唐沸畔⑻砑?、刪除、查找等相關(guān)操作技巧,需要的朋友可以參考下
    2019-11-11
  • Java實(shí)現(xiàn)文件上傳到服務(wù)器本地并通過(guò)url訪問(wèn)的方法步驟

    Java實(shí)現(xiàn)文件上傳到服務(wù)器本地并通過(guò)url訪問(wèn)的方法步驟

    最近項(xiàng)目中使用到了文件上傳到服務(wù)器的功能,下面這篇文章主要給大家介紹了關(guān)于Java實(shí)現(xiàn)文件上傳到服務(wù)器本地并通過(guò)url訪問(wèn)的方法步驟,文中通過(guò)圖文以及實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-04-04
  • Java布隆過(guò)濾器的原理和實(shí)現(xiàn)分析

    Java布隆過(guò)濾器的原理和實(shí)現(xiàn)分析

    數(shù)組、鏈表、樹(shù)等數(shù)據(jù)結(jié)構(gòu)會(huì)存儲(chǔ)元素的內(nèi)容,一旦數(shù)據(jù)量過(guò)大,消耗的內(nèi)存也會(huì)呈現(xiàn)線性增長(zhǎng)所以布隆過(guò)濾器是為了解決數(shù)據(jù)量大的一種數(shù)據(jù)結(jié)構(gòu)。本文就來(lái)和大家詳細(xì)說(shuō)說(shuō)布隆過(guò)濾器的原理和實(shí)現(xiàn),感興趣的可以了解一下
    2022-10-10
  • xxl-job的部署及springboot集成使用示例詳解

    xxl-job的部署及springboot集成使用示例詳解

    XXL-Job是一個(gè)分布式任務(wù)調(diào)度平臺(tái),可進(jìn)行任務(wù)調(diào)度、管理和監(jiān)控,并提供任務(wù)分片、失敗重試、動(dòng)態(tài)分配等功能,這篇文章主要介紹了xxl-job的部署及springboot集成使用,需要的朋友可以參考下
    2023-06-06
  • Go反射底層原理及數(shù)據(jù)結(jié)構(gòu)解析

    Go反射底層原理及數(shù)據(jù)結(jié)構(gòu)解析

    這篇文章主要介紹了Go反射底層原理及數(shù)據(jù)結(jié)構(gòu)解析,反射的實(shí)現(xiàn)和interface的組成很相似,都是由“類(lèi)型”和“數(shù)據(jù)值”構(gòu)成,下面小編分享更多相關(guān)內(nèi)容需要的小伙伴可以參考一下
    2022-06-06
  • Spring事務(wù)管理方法步驟解析

    Spring事務(wù)管理方法步驟解析

    這篇文章主要介紹了Spring事務(wù)管理方法步驟解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-09-09
  • JAVA獲取CLASSPATH路徑的方法詳解

    JAVA獲取CLASSPATH路徑的方法詳解

    這篇文章主要介紹了Java 中獲取類(lèi)路徑 classpath 的方法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2016-09-09
  • Java中List排序的3種常見(jiàn)方法總結(jié)

    Java中List排序的3種常見(jiàn)方法總結(jié)

    在Java編程中List對(duì)象的排序是一個(gè)常見(jiàn)的需求,List接口提供了多種排序方法,這篇文章主要給大家介紹了關(guān)于Java中List排序的3種常見(jiàn)方法,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-08-08

最新評(píng)論