從零開始學java--泛型

泛型

目錄

泛型

引入

泛型類

泛型與多態

泛型方法

泛型的界限

類型擦除

函數式接口

Supplier供給型函數式接口:

Consumer消費型函數式接口:

Function函數型函數式接口:

Predicate斷言式函數式接口:

判空包裝


引入

學生成績可能是數字類型,也可能是字符串類型,如何存放可能出現的兩種類型呢:

public class Score {String name;String id;Object value;  //因為Object是所有類型的父類,因此既可以存放Integer也能存放Stringpublic Score(String name,String id,Object value){this.name=name;this.id=id;this.value=value;}
}

以上方法雖然解決了多種類型存儲的問題,但是Object類型在編譯階段并不具有良好的類型判斷能力,很容易出現:

    public static void main(String[] args) {Score score=new Score("數學","aa","優秀"); //是String類型的Integer number=(Integer) score.getValue();//獲取成績需要進行強制類型轉換,雖然并不是一開始的類型,但是編譯不會報錯}

由于是Object類型,所以并不能直接判斷存儲的到底是String還是Integer,取值只能進行強制類型轉換,顯然無法在編譯期確定類型是否安全,項目中代碼量非常大,進行類型比較又會導致額外的開銷和增加代碼量,如果不比較又容易出現類型轉換異常,代碼的健壯性有所欠缺。

為了解決以上問題,JDK5新增了泛型,它能夠在編譯階段檢查類型安全,大大提升開發效率。


泛型類

定義泛型類:


public class Score<T> { //泛型類需要使用<>,在里面添加1-N個類型變量String name;String id;T value;  //T會根據使用時提供的類型自動變成對應類型public Score(String name,String id,T value){  //這里的T可以是任何類型,但是一旦確定就不能修改了this.name=name;this.id=id;this.value=value;}
}
public static void main(String[] args) {Score<String> score=new Score<>("數學","aa","優秀");//使用時跟上<>并在其中填寫明確要使用的類型}

泛型將數據類型控制在了編譯階段, 在編寫代碼時就能明確泛型的類型,類型不符合將無法編譯通過。

1、因為是具體使用對象時才會明確具體類型,所以說靜態方法中不能用。

2、方法中使用待確定類型的變量時,因為不明確類型則會默認這個變量是一個Object類型的變量(即不能使用String等類型中的方法)。可對其進行強制類型轉換但沒必要。

3、不能通過這個不確定的類型變量直接創建對象和對應的數組。

4、具體類型不同的泛型類變量,不能使用不同的變量進行接收。

5、如果要讓某個變量支持引用確定了任意類型的泛型,可以使用?通配符。?

public static void main(String[] args) {Score<String> score=new Score<>("數學","aa","優秀");Score<?>score1=score;}

?如果使用通配符,由于類型不確定,所以說具體類型同樣會變成Object。

6、泛型變量可以定義多個,多個類型變量用,隔開。在使用時需要將這三種類型都進行明確指令。

7、泛型只能確定為一個引用類型,不支持基本類型。

要存放基本數據類型的值,我們只能使用對應的包裝類。

如果是基本類型的數組,因為數組本身是引用類型,所以是可以的。


泛型與多態

不只是類,包括接口、抽象類都可以支持泛型:

public static void main(String[] args) {Score<String> score=new Score<>("數學","aa","優秀");Score<?>score1=score;}

當子類實現此接口時,我們可以選擇在實現類明確泛型類型:

public static void main(String[] args) {A a=new A();Integer i=a.study();}static class A implements Study<Integer>{
//在實現接口或是繼承父類時,如果子類是一個普通類,那么可以直接明確對應類型@Overridepublic Integer study() {return 0;}}

也可以繼續使用泛型:

public static void main(String[] args) {A<Integer> a=new A<>();Integer i=a.study();}static class A<T> implements Study<T> {
//讓子類繼續為一個泛型類,那么可以不明確@Overridepublic T study() {return null;}}

?繼承:

static class A<T>{}static class B extends A<String>{}

?


泛型方法

泛型變量不僅僅在泛型類中使用,也可以定義泛型方法。

當某個方法(無論是靜態方法還是成員方法)需要接受的參數類型不確定時,我們可以使用泛型來表示:

public static void main(String[] args) {String str=test("10");}public static <T>T test(T t){ //在返回值類型前添加<>并填寫泛型變量表示這是一個泛型方法return t;}

泛型方法會在使用時自動確定泛型類型,比如我們定義的是類型T作為參數,同樣的類型T作為返回值,實際傳入的參數是一個字符串類型的值,那么T就會自動變成String類型,因此返回值也是String類型。

泛型方法在很多工具類中也有,比如說Arrays的排序方法:

public static void main(String[] args) {Integer[] arr = {1, 3, 2, 7, 4, 9, 0};//不能比較基本數據類型intArrays.sort(arr, new Comparator<Integer>() {//通過創建泛型接口的匿名內部類,來自定義排序規則,因為匿名內部類就是接口的實現類,所以這里就明確了類型@Overridepublic int compare(Integer o1, Integer o2) {  //這個方法會在執行排序時被調用(別人調用我們的實現)//想要讓數據從大到小排列:return o2-o1;//compare方法要求返回一個int來表示兩個數的大小關系,大于0表示大于,小于0表示小于//如果o2比o1大,那么應該排在前面,所以說返回正數表示大于}});System.out.println(Arrays.toString(arr));}

可替換為Lambda表達式:

public static void main(String[] args) {Integer[] arr = {1, 3, 2, 7, 4, 9, 0};Arrays.sort(arr, (o1, o2) -> o2-o1);System.out.println(Arrays.toString(arr));}


泛型的界限

若現在沒有String類型的成績了,但是成績依然可能是整數或小數,我們不希望將泛型指定為除數字類型外的其他類型,就需要使用到泛型的上界定義。

只需要在泛型變量的后面添加extends關鍵字即可指定上界:


public class Score<T extends Number> { //設定類型參數上界,必須是Number或Number的子類String name;String id;T value;public Score(String name,String id,T value){this.name=name;this.id=id;this.value=value;}public T getValue() {return value;}
}

泛型通配符也支持泛型的界限:

public static void main(String[] args) {Score<? extends Integer>score=new Score<>("xm","11",10);}

下界只適用于通配符,對于類型變量來說是不支持的。

public static void main(String[] args) {Score<? super Object>score=new Score<>("xm","11",10);}

entends定義的只能存放它自己及其子類,super定義的只能存放它自己及其父類。

限定上界后使用這個對象的泛型成員:

public static void main(String[] args) {Score<? extends Number>score=new Score<>("xm","11",10);Number o=score.getValue();  //此時雖然使用的是通配符,但是不再是Object類型,而是對應的上界}

?限定下界的話,因為還有可能是Object,所以說依然和之前一樣:

public static void main(String[] args) {Score<? super Number>score=new Score<>("xm","11",10);Object o=score.getValue();}

?


類型擦除

實際上在Java中并不是真的有泛型類型,因為所有的對象都是一個普通的類型,一個泛型類型編譯之后,實際上會直接使用默認的類型。

在編譯的過程當中,將所有的T替換為Object這種機制,我們稱為:擦除機制。

如果我們給類型變量設定了上界,那么會從默認類型變成上界定義的類型。

泛型其實僅僅是在編譯階段進行類型檢查,當程序在運行時,并不會真的去檢查對應類型,所以哪怕我們不指定類型也可以使用。

擦除機制其實就是為了方便使用后面集合類(否則每次都要強制類型轉換)同時為了向下兼容采取的方案,因此泛型的使用會有一些限制:

首先,在進行類型判斷時,不允許使用泛型,只能使用原始類型:

    public static void main(String[] args) {Test<String> test =new Test<>();System.out.println(test instanceof Test);}

其次,泛型不支持創建參數化類型數組的:

只不過只是把它當做泛型類型的數組還是可以用的:


函數式接口

@FunctionalInterface 函數式接口都會打上這樣的注解

滿足Lambda表達式的需求有且僅有一個需要去實現(未實現)的方法。

函數式接口就是JDK1.8專門提供好的用于Lambda表達式的接口,這些接口都可以直接使用Lambda表達式。以下主要介紹四個主要的函數式接口:

Supplier供給型函數式接口:

這個接口是專門用于供給使用的,其中只有一個get方法用于獲取需要的對象。

@FunctionalInterface
public interface Supplier<T> {/*** Gets a result.** @return a result*/T get(); //實現此方法,實現供給功能
}
    public static void main(String[] args) {Supplier<Student> studentSupplier= Student::new;studentSupplier.get().hello();}public static class Student{public void hello(){System.out.println("我是學生");}}

Consumer消費型函數式接口:

這個接口專門用于消費某個對象。

@FunctionalInterface
public interface Consumer<T> {void accept(T t); //這個方法用于消費,沒有返回值default Consumer<T> andThen(Consumer<? super T> after) {  //默認實現,這個方法便于我們連續使用此消費接口Objects.requireNonNull(after);return (T t) -> { accept(t); after.accept(t); };}
}
public class Main {
//專門消費Student對象的Consumerprivate static final Consumer<Student> STUDENT_COMPARATOR=student->System.out.println(student+"看不懂");public static void main(String[] args) {Student student=new Student();STUDENT_COMPARATOR.accept(student);}public static class Student{public void hello(){System.out.println("我是學生");}}
}
    public static void main(String[] args) {Student student=new Student();STUDENT_COMPARATOR   //可以使用andThen方法繼續調用,將消費之后的操作以同樣的方式預定好.andThen(student1 -> System.out.println("后續操作")).accept(student);}//輸出
com.test.Main$Student@404b9385看不懂
后續操作

Function函數型函數式接口:

這個接口消費一個對象,然后會向外供給一個對象(前兩個的融合體)

@FunctionalInterface
public interface Function<T, R> {R apply(T t);  //這里一共有兩個類型參數,其中一個是接受的參數類型,另一個是返回的結果類型default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {Objects.requireNonNull(before);return (V v) -> apply(before.apply(v));}default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {Objects.requireNonNull(after);return (T t) -> after.apply(apply(t));}static <T> Function<T, T> identity() {return t -> t;}
}

apply方法:?

//這里實現了一個簡單的功能,將傳入的int參數轉換為字符串的形式private static final Function<Integer,String>INTEGER_STRING_FUNCTION=Objects::toString;
//Integer輸入,String輸出?public static void main(String[] args) {String str=INTEGER_STRING_FUNCTION.apply(10);System.out.println(str);}

使用compose將指定函數式的結果作為當前函數式的實參:(compose是前置工作)

    public static void main(String[] args) {String str=INTEGER_STRING_FUNCTION.compose((String s)->s.length())  //將此函數式的返回值作為當前實現的實參.apply("aaa");  //傳入上面函數式需要的參數System.out.println(str);//String ->Integer ->String// aaa   ->  3     -> "3"}//輸出3

andThen可以將當前實現的返回值進行進一步的處理,得到其他類型的值:(后續工作)

    public static void main(String[] args) {Boolean str=INTEGER_STRING_FUNCTION.andThen(String::isEmpty)  //在執行完后,返回值作為參數執行andThen內的函數式,最后得到的結果就是最終的結果了.apply(10);System.out.println(str);}
//輸出false

還提供了一個將傳入參數原樣返回的實現:

    public static void main(String[] args) {Function<String,String>function=Function.identity();System.out.println(function.apply("aaaa"));}
//輸出aaaa

Predicate斷言式函數式接口:

接收一個參數,然后進行自定義并返回一個boolean結果。

@FunctionalInterface
public interface Predicate<T> {boolean test(T t);  //要實現的方法default Predicate<T> and(Predicate<? super T> other) {Objects.requireNonNull(other);return (t) -> test(t) && other.test(t);}default Predicate<T> negate() {return (t) -> !test(t);}default Predicate<T> or(Predicate<? super T> other) {Objects.requireNonNull(other);return (t) -> test(t) || other.test(t);}static <T> Predicate<T> isEqual(Object targetRef) {return (null == targetRef)? Objects::isNull: object -> targetRef.equals(object);}
}
import java.util.function.Predicate;public class Main {private static final Predicate<Student> STUDENT_PREDICATE=student -> student.score>=60;public static void main(String[] args) {Student student=new Student();student.score=80;if (STUDENT_PREDICATE.test(student)){  //test方法的返回值是一個boolean結果System.out.println("及格了");}else {System.out.println("不及格");}}public static class Student{int score=100;public void hello(){System.out.println("我是學生");}}
}


判空包裝

判空包裝類Optional,這個類可以很有效的處理空指針問題。

    public static void main(String[] args) {test(null);}public static void test(String str){  //傳入字符串,如果不是空串就打印長度if(str == null)return; //沒有這句若傳入null會出現空指針異常錯誤if(!str.isEmpty()){System.out.println("長度為"+str.length());}}

用Optional類處理上述問題:

    public static void test(String str){Optional.ofNullable(str)  //將傳入的對象包裝進Optional中.ifPresent(s -> System.out.println("長度為"+s.length()));//如果不為空(ifPresent)則執行這里的Consumer實現}

其他的一些使用:

//不為空輸出字符串,為空...public static void test(String str){String s1=Optional.ofNullable(str).orElse("為null的備選情況");System.out.println(s1);}
//將包裝的類型直接轉換為另一種類型public static void main(String[] args) {test("aaaaa");}public static void test(String str){Integer i=Optional.ofNullable(str).map(s -> s.length()).get();System.out.println(i);}
//輸出5

......

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/901068.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/901068.shtml
英文地址,請注明出處:http://en.pswp.cn/news/901068.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

5?? Coze+AI應用基礎教學(2025年全新版本)

目錄 一、了解應用開發 1.1 扣子應用能做什么 1.2 開發流程 1.3 開發環境 二、快速搭建一個AI應用 2.1 AI翻譯應用介紹 2.2 設計你的應用功能 2.3 創建 AI 應用項目 2.4 編寫業務邏輯(新建工作流) 2.5 搭建用戶界面 2.6 效果測試 2.7 發布應用 一、了解應用開發 …

工會成立100周年紀念,開發職工健身AI運動小程序、APP方案推薦

時光荏苒&#xff0c;轉眼間2025年五一將至&#xff0c;這一年對于中華全國總工會而言&#xff0c;具有非凡的歷史意義——它將迎來成立100周年的輝煌時刻。為了慶祝這一盛事&#xff0c;各級工會組織將精心籌備了一系列豐富多彩、形式多樣的紀念活動&#xff0c;旨在展現工會百…

【深度學習】Ubuntu 服務器配置開源項目FIGRET(PyTorch、torch-scatter、torch-sparse、Gurobi 安裝)

開源項目網址&#xff1a;https://github.com/FIGRET/figret 該項目在SIGCOMM2024發表&#xff0c;用深度學習方法處理流量工程中的突發問題 1. 創建新的 Conda 環境 使用國內鏡像源創建環境? conda create -n figret python3.8.0 --override-channels -c https://mirrors.…

【SpringCloud】從入門到精通(上)

今天主播我把黑馬新版微服務課程MQ高級之前的內容都看完了&#xff0c;雖然在看視頻的時候也記了筆記&#xff0c;但是看完之后還是忘得差不多了&#xff0c;所以打算寫一篇博客再溫習一下內容。 課程坐標:黑馬程序員SpringCloud微服務開發與實戰 微服務 認識單體架構 單體架…

MySQL中動態生成SQL語句去掉所有字段的空格

在MySQL中動態生成SQL語句去掉所有字段的空格 在數據庫管理過程中&#xff0c;我們常常會遇到需要對表中字段進行清洗和整理的情況。其中&#xff0c;去掉字段中的空格是一項常見的操作。當表中的字段數量較少時&#xff0c;我們可以手動編寫 UPDATE 語句來處理。但如果表中包…

【Grok 大模型深度解析】第二期:架構探秘與訓練哲學

在上一期的內容中,我們對 Grok 大模型從技術溯源的角度,了解了它從 Transformer 架構局限性出發,邁向混合架構創新的歷程,同時也梳理了從 Grok - 1 到 Grok - 3 的版本迭代所帶來的技術躍遷以及其獨特的差異化優勢。這一期,我們將深入到 Grok 大模型的架構內部,探究其精妙…

c# 使用NPOI將datatable的數據導出到excel

以下是使用 NPOI 庫 將 DataTable 數據導出到 Excel 的詳細步驟和代碼示例(支持 .xls 和 .xlsx 格式): 步驟 1:安裝 NPOI NuGet 包 Install-Package NPOI Install-Package NPOI.OOXML # 若需導出 .xlsx 格式 步驟 2:完整代碼實現 using NPOI.SS.UserModel; using NPOI.…

基于SpringBoot的求職招聘網站系統(源碼+數據庫)

473基于SpringBoot的求職招聘網站系統&#xff0c;本系統共分為2個角色&#xff1a;系統管理員、用戶&#xff0c;主要功能如下 【前臺功能】 用戶角色功能&#xff1a; 1. 注冊和登錄&#xff1a;注冊賬戶并登錄系統&#xff0c;以便訪問更多功能。 2. 個人信息管理&#x…

CSS 過渡與變形:讓交互更絲滑

在網頁設計中&#xff0c;動效能讓用戶交互更自然、流暢&#xff0c;提升使用體驗。本文將通過 CSS 的 transition&#xff08;過渡&#xff09;和 transform&#xff08;變形&#xff09;屬性&#xff0c;帶你入門基礎動效設計&#xff0c;結合案例演示如何實現顏色漸變、元素…

rqlite:一個基于SQLite構建的分布式數據庫

今天給大家介紹一個基于 SQLite 構建的輕量級分布式關系型數據庫&#xff1a;rqlite。 rqlite 基于 Raft 協議&#xff0c;結合了 SQLite 的簡潔性以及高可用分布式系統的穩健性&#xff0c;對開發者友好&#xff0c;操作極其簡便&#xff0c;其核心設計理念是以最低的復雜度實…

mujoco graspnet 仿真項目的復現記錄

開源項目&#xff1a;https://gitee.com/chaomingsanhua/manipulator_grasp 復現使用的配置&#xff1a;linux系統ubuntu20.04 項目配置記錄&#xff1a; git clone 對應的code后&#xff1a; 需要在graspnet-baseline文件夾中繼續拉取文件&#xff0c;指令記錄&#xff1a;…

【js面試題】new操作做了什么?

這些年也面試了一些外包同事&#xff0c;不知道其他面試官的想法&#xff0c;但就我而言&#xff0c;我更喜歡聽到的是口述代碼的方式&#xff1a; 比如下述代碼 function Animal(age) {this.age age; // 設置新對象的屬性 }const cat new Animal("8");最有效的回…

freecad內部python來源 + pip install 裝包

cmake來源&#xff1a; 只能find默認地址&#xff0c;我試過用虛擬的python地址提示缺python3config.cmake python解釋器位置&#xff1a; python控制臺位置&#xff1a; pip install 裝包&#xff1a; module_to_install "your pakage" import os import FreeCAD …

樹和圖論【詳細整理,簡單易懂!】(C++實現 藍橋杯速查)

樹和圖論 樹的遍歷模版 #include <iostream> #include <cstring> #include <vector> #include <queue> // 添加queue頭文件 using namespace std;const int MAXN 100; // 假設一個足夠大的數組大小 int ls[MAXN], rs[MAXN]; // 定義左右子樹數…

展訊android15源碼編譯之apk單編

首先找到你要單編的apk生成的路徑&#xff1a; sys\out_system\target\product\ussi_arm64\system_ext\app\HelloDemo\HelloDemo.apk接著打開下面這個文件&#xff1a; sys\out_system\ussi_arm64_full-userdebug-gms.system.build.log在里面找關鍵字"Running command&q…

如何關閉MacOS中鼠標滾輪滾動加速

一、背景 想要關閉滾輪的 “滾動加速”&#xff0c;即希望滾動了多少就對應滾動頁面固定行數&#xff0c;現在macOS是加速滾動的&#xff0c;即滾動相同的角度會根據你滾動滾輪的速度不同最終頁面滾動的幅度不同。這點很煩&#xff0c;常導致很難定位。 macOS本身的設置是沒有…

河北工程大學e2e平臺,python

題目&#xff0c;選擇題包100分&#xff01; 題目&#xff0c;選擇題包100分&#xff01; 題目&#xff0c;選擇題包100分&#xff01; 聯系&#x1f6f0;&#xff1a;18039589633

【藍橋杯】貪心算法

1. 區間調度 1.1. 題目 給定個區間,每個區間由開始時間start和結束時間end表示。請選擇最多的互不重疊的區間,返回可以選擇的區間的最大數量。 輸入格式: 第一行包含一個整數n,表示區間的數量 接下來n行,每行包含兩個整數,分別表示區間的開始時間和結束時間 輸出格式:…

一維差分數組

2.一維差分 - 藍橋云課 問題描述 給定一個長度為 n 的序列 a。 再給定 m 組操作&#xff0c;每次操作給定 3 個正整數 l, r, d&#xff0c;表示對 a_{l} 到 a_{r} 中的所有數增加 d。 最終輸出操作結束后的序列 a。 ??Update??: 由于評測機過快&#xff0c;n, m 于 20…

二分答案----

二分答案 - 題目詳情 - HydroOJ 問題描述 給定一個由n個數構成的序列a&#xff0c;你可以進行k次操作&#xff0c;每次操作可以選擇一個數字&#xff0c;將其1&#xff0c;問k次操作以后&#xff0c;希望序列里面的最小值最大。問這個值是多少。 輸入格式 第一行輸入兩個正…