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