小編這次要給大家分享的是詳解JAVA泛型,文章內容豐富,感興趣的小伙伴可以來了解一下,希望大家閱讀完這篇文章之后能夠有所收獲。
我們提供的服務有:網站制作、成都網站建設、微信公眾號開發、網站優化、網站認證、印臺ssl等。為成百上千家企事業單位解決了網站和推廣的問題。提供周到的售前咨詢和貼心的售后服務,是有科學管理、有技術的印臺網站制作公司
泛型的概念:Java泛型(generics)是JDK1.5中引入的一個新特性,泛型提供了編譯時的類型安全監測機制,該機制允許我們在編譯時檢測到非法的類型數據結構。
泛型的本質就是類型參數化,也就是所操作的數據類型被指定為一個參數。
使用泛型的好處:
1 在編譯期間提供了類型檢查
2 取數據時無須進行類型裝換
語法:
class 類名稱 <泛型標識,泛型標識,泛型標識,...> {
private 泛型標識 變量名;
// ...
}
常用的泛型標識:T、E、K、V
使用語法:
類名 <具體的數據類型> 對象名 = new 類名<具體的數據類型>();
JDK 1.7 之后,后面的 <> 中的具體的數據類型可以省略不寫。
定義一個簡單的泛型類:
/**
* 泛型類 T:類型形參,在類創建對象時,指定具體的數據類型
* @author rainszj
* 2020/3/19
*/
public class GenericDemo01<T> {
private T value;
public GenericDemo01() {
}
public GenericDemo01(T value) {
this.value = value;
}
@Override
public String toString() {
return "GenericDemo01{" +
"value=" + value +
'}';
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
測試一下:
public class Test {
public static void main(String[] args) {
// 在創建對象時指定具體的數據類型
GenericDemo01<String> genericDemo01 = new GenericDemo01<>("java");
// 泛型類不支持基本數據類型,但可以使用基本類型對應的包裝類
GenericDemo01<Integer> genericDemo02 = new GenericDemo01<>(1);
// 在泛型類對象時,不指定具體的數據類型,將會使用Object類型來接收
// 同一個泛型類,根據不同數據類型創建的對象,本質上是同一類型,公用同一個類模板
// class com.rainszj.GenericDemo01
System.out.println(genericDemo01.getClass());
// class com.rainszj.GenericDemo01
System.out.println(genericDemo02.getClass());
// true
System.out.println(genericDemo01.getClass() == genericDemo02.getClass());
}
}
注意事項:
泛型類,如果沒有指定具體的數據類型,按Object類型來接收
泛型的類型參數只能是類類型,也就是引用數據類型,不能是基本數據類型
泛型類型在邏輯上可以看成是多個不同的類型,但實際上都是相同類型
/**
* 抽獎池
*
* @author rainszj
* 2020/3/19
*/
public class ProductGetter<T> {
// 獎品
private T product;
private ArrayList<T> list = new ArrayList<>();
/**
* 添加獎品
*
* @param product
*/
public void addProduct(T product) {
list.add(product);
}
/**
* 抽取隨機獎品
*
* @return
*/
public T getProduct() {
return list.get(new Random().nextInt(list.size()));
}
@Override
public String toString() {
return "ProductGetter{" +
"product=" + product +
'}';
}
}
public static void main(String[] args) {
ProductGetter<String> productGetter1 = new ProductGetter<>();
// 獎品類型 禮物
String[] products1 = {"華為手機", "蘋果手機", "掃地機器人", "微波爐"};
// 添加獎品
for (int i = 0, length = products1.length; i < length; i++) {
productGetter1.addProduct(products1[i]);
}
// 獲取獎品
String product1 = productGetter1.getProduct();
System.out.println("恭喜您抽中了," + product1.toString());
ProductGetter<Integer> productGetter2 = new ProductGetter<>();
// 獎品類型 money
Integer[] products2 = {1000, 3000, 10000, 500};
for (Integer money : products2) {
productGetter2.addProduct(money);
}
Integer product2 = productGetter2.getProduct();
System.out.println("恭喜您抽中了," + product2.toString());
}
子類也是泛型類,子類的泛型標識 T 要和父類的泛型標識 T 保持一致,或者是包含關系,子類的泛型標識包含父類的泛型標識
class ChildGeneric<T> extends ParentGeneric<T>
class ChildGeneric<T, E> extends ParentGeneric<T>
子類不是泛型類,父類要明確泛型的數據類型
class ChildGeneric extends ParentGeneric<String>
語法:
interface 接口名稱 <泛型標識,泛型標識,...> {
泛型標識 方法名();
}
實現泛型接口的類,不是泛型類,需要明確實現泛型接口的數據類型
public class Apple implements Generic<String> {}
實現類也是泛型類,實現類和接口的泛型類型要一致,或者是包含關系,實現類的泛型標識包含泛型接口的泛型標識
public class Apple<K> implements Generic<K> {}
public class Apple<K, V> implements Generic<K> {}
定義一個泛型接口
public interface Generic<K> {
K getKey();
}
實現其中方法:
/**
* 泛型接口的實現類,是一個泛型類,
* 那么要保證實現接口的泛型類的泛型標識包含泛型接口的泛型標識
*/
public class Pair<K, V> implements Generic<K> {
private K key;
private V value;
public Pair() {
}
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
@Override
public K getKey() {
return key;
}
public V getValue() {
return value;
}
@Override
public String toString() {
return "Pair{" +
"key=" + key +
", value=" + value +
'}';
}
}
測試:
public class MyTest {
public static void main(String[] args) {
Pair<String, Integer> pair = new Pair<>("數學", 100);
System.out.println(pair.toString());
// Pair{key=數學, value=100}
}
}
泛型類,是在實例化類時指明泛型的具體類型。
泛型方法,是在調用方法時,指明泛型的具體類型。
語法:
修飾符 <T,E,...> 返回值類型 方法名(形參列表) {
// 方法體...
}
public
與返回值中間 <T,E,...>
(泛型列表)非常重要,可以理解為聲明此方法為泛型方法。
只有聲明了 <T,E,...>
的方法才是泛型方法,泛型類中使用了泛型的成員方法并不是泛型方法
<T>
表明該方法將使用泛型類型 T,此時才可以在方法中使用泛型類型 T。
public class ProductSetter<T> {
private T product;
private Random random= new Random();
private ArrayList<T> list = new ArrayList<>();
public void addProduct(T product) {
list.add(product);
}
/**
* @param list
* @param <E> 泛型方法的類型,是在調用泛型方法時確定的
* @return
*/
public <E> E getProduct(ArrayList<E> list) {
return list.get(random.nextInt(list.size()));
}
public T getProduct() {
return list.get(random.nextInt(list.size()));
}
@Override
public String toString() {
return "ProductSetter{" +
"product=" + product +
'}';
}
}
測試:
public static void main(String[] args) {
ProductSetter<String> productSetter = new ProductSetter<>();
String[] products1 = {"華為手機", "蘋果手機", "掃地機器人", "微波爐"};
for (int i = 0; i < products1.length; i++) {
productSetter.addProduct(products1[i]);
}
System.out.println(productSetter.getProduct());
ArrayList<String> list1 = new ArrayList<>();
list1.add("華碩電腦");
list1.add("蘋果電腦");
list1.add("華為電腦");
String product1 = productSetter.getProduct(list1);
System.out.println(product1 + "\t" + product1.getClass().getSimpleName());
// 華為電腦 String
ArrayList<Integer> list2 = new ArrayList<>();
list2.add(1);
list2.add(2);
list2.add(3);
Integer product2 = productSetter.getProduct(list2);
System.out.println(product2 + "\t" + product2.getClass().getSimpleName());
// 2 Integer
}
public static <T, E, K> void pringType(T k1, E k2, K k3) {
System.out.println(k1 + "\t" + k1.getClass().getSimpleName());
System.out.println(k2 + "\t" + k2.getClass().getSimpleName());
System.out.println(k3 + "\t" + k3.getClass().getSimpleName());
}
// 方法的調用
ProductSetter.pringType(1, "hello", false);
// 輸出結果
1 Integer
hello String
false Boolean
注意:
// 在泛型類中無法添加靜態的 帶有泛型成員方法,但可以添加靜態的 泛型方法
public class Test<T> {
// 帶有泛型的成員方法
// 錯誤
public static T getKey(T key) {
return key;
}
// 泛型方法
// 正確
public static <E> E getKey(E key) {
return key;
}
}
public class MyTest {
public static void main(String[] args) {
MyTest.print(1, 2, 3);
}
/**
* 泛型方法中的可變長參數
* @param value
* @param <E>
*/
public static <E> void print(E ... value) {
for (int i = 0; i < value.length; i++) {
System.out.println(value[i]);
}
}
}
總結:
泛型方法能使方法獨立于類而產生變化。
如果 static
方法要使用泛型能力,就必須使其成為泛型方法。
類型通配符一般是使用 ?
代替具體的類型實參。
類型通配符是類型實參,而不是類型形參。
我們先來定義一個簡單的泛型類:
public class Box<T> {
private T width;
public static void showBox(Box<Number> box) {
Number width = box.getWidth();
System.out.println(width);
}
public T getWidth() {
return width;
}
public void setWidth(T width) {
this.width = width;
}
}
main方法:
public static void main(String[] args) {
Box<Number> box1 = new Box<Number>();
box1.setWidth(100);
showBox(box1);
}
當我們在 main 方法中增加這一段代碼時,就會報錯
Box<Integer> box2 = new Box<>();
box2.setWidth(200);
showBox(box2);
雖然 Integer 類繼承自 Number 類,但在類型通配符中不存在繼承這一概念!
也許你會使用方法的重載,但是 在同一個泛型類中,根據不同數據類型創建的對象,本質上是同一類型,公用同一個類模板,所以無法通過方法的重載,傳遞不同的泛型類型。
這時可以使用類型通配符 ?
,來代表具體的類型實參!
public static void showBox(Box<?> box) {
Object width = box.getWidth();
System.out.println(width);
}
在我們上面的showBox()代碼中,發現 box.getWidth()
得到的還是Object類型,這和我們不使用類型通配符,得到的結果是一樣的。這時我們可以使用類型通配符的上限。
語法:
類/接口 <? entends 實參類型>
要求該泛型的類型,只能是實參類型,或者是實參類型的子類類型。
public static void showBox(Box<? extends Number> box) {
Number width = box.getWidth();
System.out.println(width);
}
public static void main(String[] args) {
Box<Integer> box2 = new Box<>();
box2.setWidth(200);
showBox(box2);
}
使用類型通配符的下限,無法得知該類型具體是指定的類型,還是該類型的子類類型,因此無法在 List 集合中執行添加該類或者該類子類的操作!
public static void showAnimal(List<? extends Cat> list) {
// 錯誤
list.add(new Cat());
list.add(new MiniCat());
}
語法
類/接口 <? super 實參類型>
要求該泛型的類型,只能是實參類型,或者是實參類型的父類類型。
下面通過 TreeSet 集合中的一個構造方法來進一步理解 類型通配符的下限
public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator));
}
首先是一個Animal類,只有一個 name 屬性
public class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Animal{" +
"name='" + name + '\'' +
'}';
}
}
然后它的一個子類,Cat添加一個屬性:age
public class Cat extends Animal {
private int age;
public Cat(String name, int age) {
super(name);
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Cat{" +
"age=" + age +
'}';
}
}
最后是 Cat 的子類,MiniCat,再添加一個屬性 level
public class MiniCat extends Cat {
private int level;
public MiniCat(String name, int age, int level) {
super(name, age);
this.level = level;
}
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
}
@Override
public String toString() {
return "MiniCat{" +
"level=" + level +
'}';
}
}
測試,首先我們要在MyTest類通過靜態內部類的方式,實現比較的接口,在構造TreeSet時,傳遞比較器
public class MyTest {
public static void main(String[] args) {
// 正常
// TreeSet<Cat> animals = new TreeSet<Cat>(new Comparator1());
// 正常
TreeSet<Cat> animals = new TreeSet<Cat>(new Comparator2());
// 報錯
// TreeSet<Cat> animals = new TreeSet<Cat>(new Comparator3());
List<Cat> list = Arrays.asList(new Cat("a", 12), new Cat("c", 9), new Cat("b", 20));
animals.addAll(list);
animals.forEach(System.out::println);
}
public static class Comparator1 implements Comparator<Animal> {
@Override
public int compare(Animal o1, Animal o2) {
return o1.getName().compareTo(o2.getName());
}
}
public static class Comparator2 implements Comparator<Cat> {
@Override
public int compare(Cat o1, Cat o2) {
return o1.getAge() - o2.getAge();
}
}
public static class Comparator3 implements Comparator<MiniCat> {
@Override
public int compare(MiniCat o1, MiniCat o2) {
return o1.getLevel() - o2.getLevel();
}
}
}
結論:
通過以上的比較,我們可以看出,類型通配符的下限,只能傳遞實參類型的或者實參類型的父類類型。
我們每次比較使用的都是 Cat 類型,但在 Comparator1
比較的是 Animal 中的 name 屬性,這是因為 我們在初始化 Cat 對象的時候,一定會先初始化 Animal 對象,也就是創建子類對象的時候,一定會先創建父類對象,所以才可以進行比較。
如果是使用 類型通配符的上限,在創建對象時,比較的是該類的子類對象中的屬性,就會造成空指針異常!也就是Comparator3
無法使用的原因, 所以在 TreeSet
中才會使用 <? super E>
,類型通配符的下限。
泛型是Java 1.5 引進的概念,在這之前是沒有泛型的,但是,泛型代碼能夠很好地和之前的代碼兼容。那是因為,泛型信息只存在編譯階段,在進入 JVM 之前,與泛型相關的信息會被擦除掉,我們稱之為——類型擦除。
先定義一個泛型類:
public class Erasure<T> {
private T key;
public T getKey() {
return key;
}
public void setKey(T key) {
this.key = key;
}
}
輸出結構:
public static void main(String[] args) {
Erasure<Integer> erasure = new Erasure<>();
Class<? extends Erasure> cls = erasure.getClass();
Field[] fields = cls.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName() + ":" + field.getType().getSimpleName()); // key:Object
}
}
可以發現在編譯完成后的字節碼文件中,T --> Object 類型
還是剛才的泛型類,只不過加了泛型的上限
public class Erasure<T extends Number> {// ...}
測試不變,輸出結果:
key:Number
當我們指定了泛型的上限時,它會將我們的泛型擦除為上限類型
同樣對泛型方法,也是一樣的道理
// 泛型方法
public <E extends List> E test(E t) {
return t;
}
Method[] methods = cls.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method.getName() + ":" + method.getReturnType().getSimpleName());
}
// 輸出結果
// getKey:Number
// test:List
// setKey:void
泛型接口
public interface Info<T> {
T test(T value);
}
泛型接口的實現類
public class InfoImpl implements Info<Integer> {
@Override
public Integer test(Integer value) {
return value;
}
}
測試
public static void main(String[] args) {
Class cls = InfoImpl.class;
Method[] methods = cls.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method.getName() + ":" + method.getReturnType().getSimpleName());
}
}
// 輸出結果:
// test:Integer
// test:Object
原本 InfoImpl 中只是實現了 Info 接口中的一個方法,但通過反射卻拿到了兩個方法。其中返回值為 Object 的方法就是橋接方法。
在編譯完成后,類型擦除的結果是這樣的:
public interface Info {
Object test(Object value);
}
public class InfoImpl implements Info {
public Integer test(Integer value) {
return value;
}
// 橋接方法:保持接口和類的實現關系
@Override
public Object test(Object value) {
return (Integer)value;
}
}
開發中,一般常用的是泛型集合
泛型數組的創建:
可以聲明帶泛型的數組引用,但是不能直接創建帶泛型數組對象。
可以通過 java.lang.reflect.Array
的 newInstance(Class<T>, int)
創建 T[ ] 數組。
// 可以創建帶泛型的數組引用
ArrayList<String>[] arrayLists1 = new ArrayList[3];
// 無法創建帶泛型的數組對象
ArrayList<String>[] arrayLists2 = new ArrayList<String>[3];
簡單使用 java.lang.reflect.Array
的 newInstance(Class<T>, int)
創建 T[ ] 數組。 封裝一個泛型數組
public class GenericArray<T> {
private T[] array;
public GenericArray(Class cls, int length) {
this.array = (T[]) Array.newInstance(cls, length);
}
public void put(int index, T item) {
this.array[index] = item;
}
public T get(int index) {
return this.array[index];
}
public T[] getArray() {
return this.array;
}
public static void main(String[] args) {
GenericArray<String> ga = new GenericArray<>(String.class, 3);
ga.put(0, "白虎");
ga.put(1, "青龍");
ga.put(2, "朱雀");
System.out.println(Arrays.toString(ga.getArray()));
}
}
反射常用的泛型類:
Class
Constructor
通過反射創建對象,帶泛型和不帶泛型
Class<Cat> catClass1 = Cat.class;
try {
Constructor<Cat> c1 = catClass1.getConstructor();
Cat cat = c1.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
Class catClass2 = Cat.class;
try {
Constructor c2 = catClass2.getConstructor();
Object cat2 = c2.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
看完這篇關于詳解JAVA泛型的文章,如果覺得文章內容寫得不錯的話,可以把它分享出去給更多人看到。
網站題目:詳解JAVA泛型
標題URL:http://m.newbst.com/article10/ghoggo.html
成都網站建設公司_創新互聯,為您提供營銷型網站建設、靜態網站、搜索引擎優化、標簽優化、云服務器、品牌網站建設
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯