概述
作為一個面向物件的編程語言,Java可以通過實作一些類,作為我們各種需求的一個模板,方便我們的使用,但有時候,這個類的范圍可能比我們想要的范圍要大,我們只想限定于滿足類的某些物件,那這樣的情況下,泛型的概念就被提出來了(非官方解釋,方便理解),
舉個例子:比如我們我們生活中的車,它可以作為一個類,但是車其實又有很多種,包括貨車,轎車,大巴車等等,而其中的轎車外觀差不多,但是又屬于不同的品牌,這些品牌有很多不一樣的地方,這里我們可以把轎車的品牌看作是泛型(類似于標簽)
通過上面的解釋,泛型的概念就比較清晰了,就是一種“型別引數”,所謂型別引數可以理解為將型別由原來的具體的型別進行引數化,類似于方法中的變數引數,此時型別也定義成引數形式(可以稱之為型別形參),然后在使用/呼叫時傳入具體的型別(型別實參),
泛型的優點,不僅僅是上面提到的,其還有下面的優點::
- 型別安全: 提高Java 程式的型別安全(泛型的主要目標),
通過知道使用泛型定義的變數的型別限制,編譯器可以驗證型別假設, - 消除強制型別轉換:消除源代碼中的許多強制型別轉換,
這使得代碼的可讀性更高了,并且還減少了錯誤
上面說到了泛型在類中的使用,其實泛型的使用遠不止于此,其還可以在在介面、方法中使用,下面就對這些分別進行介紹
泛型類
所謂泛型類就是把當我們在宣告類時,類中的有些成員的型別并不是確定,然后我們可以把泛型定義在類上,當使用該類的時候,再把不確定成員的型別明確下來,
語法格式
【修飾符】 class 類名<型別變數串列>{
//類體
}
注: <型別變數串列>:可以是一個或多個型別變數,一般都是使用單個的大寫字母表示,例如:、<K,V>等,
<型別變數串列>中的型別變數不能用于靜態成員上,
泛型類的使用
使用這種類似于引數化型別的類時,在創建類的物件時候,我們需要注意:
- 指定型別變數對應的實際型別引數
- 實際型別引數必須是參考資料型別,不能是基本資料型別
注:指定泛型實參時,必須左右兩邊一致,不存在多型現象(右邊的可以省略不寫)
代碼示例
/*
泛型類的宣告與使用
*/
public class Demo1 {
public static void main(String[] args) {
//泛型類的使用(<T>里面只能是參考型別)
Student<Double> student1 = new Student<>("學生1",99.5);
Student<String> student2 = new Student<>("學生2","優秀");
Student<Character> student3 = new Student<>("學生3",'A');
//輸出結果
System.out.println(student1);
System.out.println(student2);
System.out.println(student3);
}
}
//泛型類的宣告
class Student<T> { //<T>這個就是泛型類的型別引數
private String name;
private T score; //使用泛型,定義分數(分數可能有double型別(99.5)、字串型別(優秀)、字符型別(‘A’)等)
//構造方法
public Student() {
}
public Student(String name, T score) {
this.name = name;
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", score=" + score +
'}';
}
}
泛型介面
泛型介面和泛型類關系,就像介面和類的關系一樣, 這里不多說,
語法格式
【修飾符】 interface 介面名<型別變數串列>{
}
注: <型別變數串列>:可以是一個或多個型別變數,一般都是使用單個的大寫字母表示,例如:、<K,V>等,
<型別變數串列>中的型別變數不能用于靜態成員上,
泛型介面的使用
使用這種類似于引數化型別的介面時,我們需要注意:
- 指定型別變數對應的實際型別引數
- 實際型別引數必須是參考資料型別,不能是基本資料型別
代碼示例
/*
泛型介面的宣告與使用
*/
public class Demo1 {
public static void main(String[] args) {
//泛型類的使用(<T>里面只能是參考型別)
Student<Double> student1 = new Student<>("學生1",99.5);
//使用泛型介面
student1.print("學生1",99.5);
}
}
//泛型類的宣告
class Student<T> implements Print<String,T>{ //<T>這個就是泛型類的,后面<String,T>是介面,多個型別變數
private String name;
private T score; //使用泛型
//構造方法
public Student() {
}
public Student(String name, T score) {
this.name = name;
this.score = score;
}
//重寫介面的方法
@Override
public void print(String s, T t) {
System.out.println("學生姓名:"+ this.name);
System.out.println("學生成績:"+ this.score);
}
}
//泛型介面的宣告
interface Print <T,V>{
//定義一個列印函式,可以列印學生姓名和成績
public void print(T t, V v);
}
型別變數的上限和下限
前面說到,我們可以使用泛型型別引數,這樣等我們進行實際使用的時候,我們可以任意使用型別,但如果想只使用某一系列的型別,泛型也是可以實作的,這就是我們說的型別變數的上限和型別變數的下限,下面進行分別介紹,
型別變數的上限
如果泛型類定義了型別變數的上限,那么該泛型類實際的型別只能是該上限型別或者其子型別別,
語法格式
泛型類和泛型方法的用法是一樣的,后面都不再做區分,
<型別變數 extends 上限1 & 上限2> //上限可以有多個
注:如果多個上限中有類有介面,那么只能有一個類,而且必須寫在最左邊,介面的話,可以多個,
如果在宣告<型別變數>時沒有指定上限,默認上限是java.lang.Object,
代碼示例
/*
型別變數的上限
*/
public class Demo2 {
public static void main(String[] args) {
Test<Double> test1 = new Test<>(77.5); //double類
// Test<String> test2 = new Test<String>(); 不是數字類的子類
Test<Integer> test3 = new Test<>(18);
test1.print(77.5);
test3.print(18);
}
}
class Test<T extends Number >{ //數字類上限,只能使用數字類及其子類
private T num;
public Test() {
}
public Test(T num) {
this.num = num;
}
public void print(T num){ //測驗方法
System.out.println(num);
}
}
型別變數的下限
如果泛型類定義了型別變數的下限,那么該泛型類實際的型別只能是該下限型別或者其父型別別,
語法格式
<? super E > // ? 代表接收E型別或者E的父型別的元素
? 是泛型類中的通配符(下面會講到,可以先看下面的再回來看這個)
代碼示例
/*
<? super 下限>
*/
public class Demo5 {
public static void main(String[] args){
C<String> c=new C<>();
c.setT("<? super 下限>");
fun1(c);
}
//測驗函式,泛型類使用了下限
public static void fun1(C<? super String> c){ //接受的資料型別只能為String、Object
System.out.println(c.getT()); //輸入測驗
}
}
class C<T>{
private T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
泛型方法
鑒于某個方法定義時,想要自己定義型別變數或者在某個靜態方法中定義型別變數的需求,JDK還提供了泛型方法的支持,即可以在某個方法定義時,自定以<型別變數>
注:前面說到類和介面上的型別形參是不能用于靜態方法
語法格式
【修飾符】 <型別變數串列> 回傳值型別 方法名(【形參串列】)【throws 例外串列】{
//方法體
}
注:- <型別變數串列>:可以是一個或多個型別變數,一般都是使用單個的大寫字母表示,例如: < T >、<K,V>等,
<型別變數>同樣也可以指定上限
代碼示例
/*
泛型方法
*/
public class Demo3 {
public static void main(String[] args) {
Test1 test = new Test1(); //創建測驗物件
test.print(12); //測驗
test.print(12.5); //測驗
}
}
class Test1{
public <T extends Number> void print(T t){ //泛型方法,可以設定上限
System.out.println("這是一個泛型方法,測驗型別:" + t);
}
}
泛型擦除
泛型擦除只是在編譯階段才會有的,在實際運行階段型別已經確定了,這個時候就沒有泛型的概念了(JVM并不知道泛型的存在),這個從有泛型資訊到沒有泛型資訊的程序稱之為“泛型擦除”,
其擦除規則如下:
- 若泛型型別沒有指定具體型別,用Object作為原始型別;
- 若有限定型別< T exnteds XClass >,使用XClass作為原始型別;
- 若有多個限定< T exnteds XClass1 & XClass2 >,使用第一個邊界型別XClass1作為原始型別;
型別通配符
通配符的意思是可以指代很多型別,這個主要使用在當我們在宣告方法時,不確定該泛型實際型別的情況,型別通配符有三種:
- <?> 任意型別
- <? extends 上限>
- <? super E>
下面對這三種通配符分別進行介紹
<?> 任意型別
當泛型使用這種 型別通配符的時候,表示可以使用任意型別
代碼示例
/*
型別通配符
*/
public class Demo4 {
public static void main(String[] args) {
// 語文老師使用時:
StudentInfo<String> stu1 = new StudentInfo<String>("張三", "良好");
// 數學老師使用時:
StudentInfo<Double> stu2 = new StudentInfo<Double>("張三", 90.5);
// 英語老師使用時:
StudentInfo<Character> stu3 = new StudentInfo<Character>("張三", 'C');
StudentInfo<?>[] arr = new StudentInfo[3]; //使用通配符
arr[0] = stu1;
arr[1] = stu2;
arr[2] = stu3;
StudentInfoPrint.print(arr); //列印輸出結果
}
}
//學生類是一個引數化的泛型類
class StudentInfo<T>{
private String name;
private T score;
public StudentInfo() {
super();
}
public StudentInfo(String name, T score) {
super();
this.name = name;
this.score = score;
}
@Override
public String toString() {
return "姓名:" + name + ", 成績:" + score;
}
}
//學生資訊列印類
class StudentInfoPrint {
//泛型方法,使用通配符
public static void print(StudentInfo<?>[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
<? extends 上限>
? 代表接收E型別或者E的子型別的元素
代碼示例
可參考上面的型別變數的上限代碼
<? super E>
? 代表接收E型別或者E的父型別的元素
代碼示例
可參考上面的型別變數的下限代碼
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/413393.html
標籤:java
