第一:參數類型用於類上案例:
編寫泛型類GenericClass:
package com.test.cgb;
/**
*
* @Description: 定義泛型類,在類名后添加 對尖括號
* 並在尖括號中填寫類型參數,參數可以有多個,多個參數使用逗號分隔
* T表示的是任意類型 type
* E表示的是元素的類型 element
* K表示的是key/value中的key
* V表示的是key/value中的value
* 通常類型參數我們都是使用大寫。
* @author: caigb
* @createTime: 2020年2月29日
* @version:
*/
public class GenericClass<T> {
public T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
編寫測試類:
public class GenericDemo {
public static void main(String[] args) {
// 參數類型為String
GenericClass<String> genericClassOfString = new GenericClass<>();
genericClassOfString.setValue("用於類上的泛型");
String str = genericClassOfString.getValue();
System.out.println("str = " + str);
// 參數類型為Integer
GenericClass<Integer> genericClassOfInteger = new GenericClass<>();
genericClassOfInteger.setValue(111);
Integer intNum = genericClassOfInteger.getValue();
System.out.println("intNum = " + intNum);
}
輸出結果為:
str = 用於類上的泛型
intNum = 111
==================================================================================================================================
第二個:泛型接口:
定義接口如:
/**
*
* @Description: 用於接口上的泛型
* @author: caigb28355
* @createTime: 2020年2月29日
* @version:
*/
public interface GenericInterface<T> {
void show(T value);
}
那么針對這個接口我們可以分別定義多種類型的實現類如:
IntegerShowGenericInterfaceImpl:
package com.test.cgb;
// 泛型類型參數為Integer
public class IntegerShowGenericInterfaceImpl implements GenericInterface<Integer>{
@Override
public void show(Integer value) {
System.out.println(" 整數型 "+ value);
}
}
StringShowGenericInterfaceImpl:
package com.test.cgb;
// 泛型類型參數為String
public class StringShowGenericInterfaceImpl implements GenericInterface<String>{
@Override
public void show(String value) {
System.out.println(" 字符串 "+ value);
}
}
編寫測試類如下:
public class GenericDemo {
public static void main(String[] args) {
GenericInterface IntFace = new IntegerShowGenericInterfaceImpl(); // 向上轉型
IntFace.show(200);
GenericInterface StringFace = new StringShowGenericInterfaceImpl(); // 向上轉型
StringFace.show("字符串");
}
// 輸出結果為:
整數型 200
字符串 字符串
==================================================================================================================================
第三:泛型方法,可以定義在泛型類中,也可以定義在普通類中。如果可以定義泛型方法,那就盡量定義泛型方法
泛型方法類定義如:
package com.test.cgb;
/**
*
* @Description: 泛型方法,可以定義在泛型類中,也可以定義在普通類中
* 定義泛型方法,在返回值前邊,修飾符后邊添加尖括號,並在其中填寫類型參數
* 如果可以定義泛型方法,那就盡量定義泛型方法
* @author: caigb
* @createTime: 2020年2月29日
* @version:
*/
public class GenericFun {
public static<T> void saveScore(T score) {
// 這是一個保存分數的方法,分數的類型可以是int,double,float...數值類型
// 我們對分數進行復雜操作......
System.out.println("score = " + score);
}
}
編寫測試類如下:
public class GenericDemo {
public static void main(String[] args) {
GenericFun.saveScore(20.0);
GenericFun.saveScore(20);
}
打印結果如下:
score = 20.0
score = 20
但是上面這個例子有個不足的就是,命名這個方法是用來保存分數的,由於使用了static<T>形式,所以可以接收所有類型的值比如字符串中文,那么就有點違背最初的想法了,如:
public static void main(String[] args) {
GenericFun.saveScore(20.0);
GenericFun.saveScore(20);
GenericFun.saveScore("保存分數的入參竟然是中文");
}
打印結果有:
score = 20.0
score = 20
score = 保存分數的入參竟然是中文
所以為了改進上面的缺陷,我們需要限制一下參數類型,於是要對引入類型參數做一下限定,這個限定可以是類,也可以是接口,而且可以同時限定多個,如果有多個,可以使用&符號連接,如果限定中既有類又有接口,那么類一定要寫在前面<T extends Number>或者<T extends Number & Comparable>如:
public class GenericFun {
// T extends Number表示T這個類型必須繼承自Number類型,也就是一定要是Number類的字類,那么也就不能是String類型了
public static<T extends Number> void saveScore(T score) {
// 這是一個保存分數的方法,分數的類型可以是int,double,float...數值類型
// 我們對分數進行復雜操作......
System.out.println("score = " + score);
}
// Double 和Float都是繼承字Number類如:
// public final class Double extends Number implements Comparable<Double> {
// /**
// * A constant holding the positive infinity of type
// * {@code double}. It is equal to the value returned by
// * {@code Double.longBitsToDouble(0x7ff0000000000000L)}.
// */
// public static final double POSITIVE_INFINITY = 1.0 / 0.0;
// }
// public final class Float extends Number implements Comparable<Float> {
// /**
// * A constant holding the positive infinity of type
// * {@code float}. It is equal to the value returned by
// * {@code Float.intBitsToFloat(0x7f800000)}.
// */
// public static final float POSITIVE_INFINITY = 1.0f / 0.0f;
}
這樣改造之后,在測試類中如果還是調用GenericFun.saveScore("保存分數的入參竟然是中文")方法的話就會報錯如:
public class GenericDemo {
public static void main(String[] args) {
GenericFun.saveScore(20.0);
GenericFun.saveScore(20);
// 下面這句報錯信息就是The method saveScore(T) in the type GenericFun is not applicable for the arguments (String)
GenericFun.saveScore("這里有中文");
}
===============================================================================================================================
泛型之類型擦出重要知識點擴展:下面讓我們一起學習下泛型是如何擦出的,我們要知道,對於JVM來說,根本沒有泛型這個概念,只有具體類型也就是說在運行期間根本就沒有GenericClass<String>,有的只是普通類和方法。JVM都會自動提供了一個相應的原始類型替換。在編譯期間必要時會插入強制類型轉換方法。
JVM在泛型編譯時,做兩件事:
1.去掉類名后的尖括號和尖括號的類型參數,剩下的名字就是對應的類名
2.對於類中使用了類型參數的變量的類型,如果沒有限制類型的情況下,使用Object替換,如果有類型限定,使用限定的類型替換。下面我們一起看看:
首先我們在工作空間找到對應的class字節碼文件路徑,然后在該路徑打開命令符窗口:

最初我們的源文件GenericClass.java代碼如:
public class GenericClass<T> {
public T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
第二步我們可以在cmd.exe窗口輸入命令javap -help可以看到各種命令:

第三步我們可以輸入輸入命令javap -c -s GenericClass.class,我們可以看到Object類

那么如果源代碼是這樣的呢?就是有類型限定的如:
public class GenericClass<T extends Number> {
public T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
那么就替換成Number了:

