亚洲精品久久久中文字幕-亚洲精品久久片久久-亚洲精品久久青草-亚洲精品久久婷婷爱久久婷婷-亚洲精品久久午夜香蕉

您的位置:首頁技術(shù)文章
文章詳情頁

深入了解JAVA泛型

瀏覽:2日期:2022-08-31 13:34:33
什么是泛型

泛型的概念:Java泛型(generics)是JDK1.5中引入的一個新特性,泛型提供了編譯時的類型安全監(jiān)測機(jī)制,該機(jī)制允許我們在編譯時檢測到非法的類型數(shù)據(jù)結(jié)構(gòu)。

泛型的本質(zhì)就是類型參數(shù)化,也就是所操作的數(shù)據(jù)類型被指定為一個參數(shù)。

使用泛型的好處:

1 在編譯期間提供了類型檢查

2 取數(shù)據(jù)時無須進(jìn)行類型裝換

泛型類、接口泛型類

語法:

class 類名稱 <泛型標(biāo)識,泛型標(biāo)識,泛型標(biāo)識,...> { private 泛型標(biāo)識 變量名; // ...}

常用的泛型標(biāo)識:T、E、K、V

使用語法:

類名 <具體的數(shù)據(jù)類型> 對象名 = new 類名<具體的數(shù)據(jù)類型>();

JDK 1.7 之后,后面的 <> 中的具體的數(shù)據(jù)類型可以省略不寫。

定義一個簡單的泛型類:

/** * 泛型類 T:類型形參,在類創(chuàng)建對象時,指定具體的數(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; }}

測試一下:

public class Test { public static void main(String[] args) { // 在創(chuàng)建對象時指定具體的數(shù)據(jù)類型 GenericDemo01<String> genericDemo01 = new GenericDemo01<>('java'); // 泛型類不支持基本數(shù)據(jù)類型,但可以使用基本類型對應(yīng)的包裝類 GenericDemo01<Integer> genericDemo02 = new GenericDemo01<>(1); // 在泛型類對象時,不指定具體的數(shù)據(jù)類型,將會使用Object類型來接收 // 同一個泛型類,根據(jù)不同數(shù)據(jù)類型創(chuàng)建的對象,本質(zhì)上是同一類型,公用同一個類模板 // 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):

泛型類,如果沒有指定具體的數(shù)據(jù)類型,按Object類型來接收

泛型的類型參數(shù)只能是類類型,也就是引用數(shù)據(jù)類型,不能是基本數(shù)據(jù)類型

泛型類型在邏輯上可以看成是多個不同的類型,但實(shí)際上都是相同類型

/** * 抽獎池 * * @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); } /** * 抽取隨機(jī)獎品 * * @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 = {'華為手機(jī)', '蘋果手機(jī)', '掃地機(jī)器人', '微波爐'}; // 添加獎品 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());}從泛型類派生子類

子類也是泛型類,子類的泛型標(biāo)識 T 要和父類的泛型標(biāo)識 T 保持一致,或者是包含關(guān)系,子類的泛型標(biāo)識包含父類的泛型標(biāo)識

class ChildGeneric<T> extends ParentGeneric<T>class ChildGeneric<T, E> extends ParentGeneric<T>

子類不是泛型類,父類要明確泛型的數(shù)據(jù)類型

class ChildGeneric extends ParentGeneric<String>泛型接口

語法:

interface 接口名稱 <泛型標(biāo)識,泛型標(biāo)識,...> { 泛型標(biāo)識 方法名();}

實(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)識包含泛型接口的泛型標(biāo)識

public class Apple<K> implements Generic<K> {}public class Apple<K, V> implements Generic<K> {}

定義一個泛型接口

public interface Generic<K> { K getKey();}

實(shí)現(xiàn)其中方法:

/** * 泛型接口的實(shí)現(xiàn)類,是一個泛型類, * 那么要保證實(shí)現(xiàn)接口的泛型類的泛型標(biāo)識包含泛型接口的泛型標(biāo)識 */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<>('數(shù)學(xué)', 100); System.out.println(pair.toString()); // Pair{key=數(shù)學(xué), value=100} }}泛型方法普通泛型方法

泛型類,是在實(shí)例化類時指明泛型的具體類型。

泛型方法,是在調(diào)用方法時,指明泛型的具體類型。

語法:

修飾符 <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> 泛型方法的類型,是在調(diào)用泛型方法時確定的 * @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 = {'華為手機(jī)', '蘋果手機(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('蘋果電腦'); 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}靜態(tài)泛型方法

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 Integerhello Stringfalse Boolean

注意:

// 在泛型類中無法添加靜態(tài)的 帶有泛型成員方法,但可以添加靜態(tài)的 泛型方法public class Test<T> { // 帶有泛型的成員方法// 錯誤public static T getKey(T key) { return key;} // 泛型方法// 正確public static <E> E getKey(E key) { return key;}}泛型方法中的可變參數(shù)

public class MyTest { public static void main(String[] args) { MyTest.print(1, 2, 3); } /** * 泛型方法中的可變長參數(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í)參,而不是類型形參。

我們先來定義一個簡單的泛型類:

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)我們在 main 方法中增加這一段代碼時,就會報(bào)錯

Box<Integer> box2 = new Box<>();box2.setWidth(200);showBox(box2);

雖然 Integer 類繼承自 Number 類,但在類型通配符中不存在繼承這一概念!

也許你會使用方法的重載,但是 在同一個泛型類中,根據(jù)不同數(shù)據(jù)類型創(chuàng)建的對象,本質(zhì)上是同一類型,公用同一個類模板,所以無法通過方法的重載,傳遞不同的泛型類型。

這時可以使用類型通配符 ?,來代表具體的類型實(shí)參!

public static void showBox(Box<?> box) { Object width = box.getWidth(); System.out.println(width);}類型通配符的上限

在我們上面的showBox()代碼中,發(fā)現(xiàn) box.getWidth()得到的還是Object類型,這和我們不使用類型通配符,得到的結(jié)果是一樣的。這時我們可以使用類型通配符的上限。

語法:

類/接口 <? 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);}

使用類型通配符的下限,無法得知該類型具體是指定的類型,還是該類型的子類類型,因此無法在 List 集合中執(zhí)行添加該類或者該類子類的操作!

public static void showAnimal(List<? extends Cat> list) { // 錯誤 list.add(new Cat()); list.add(new MiniCat());}類型通配符的下限

語法

類/接口 <? super 實(shí)參類型>

要求該泛型的類型,只能是實(shí)參類型,或者是實(shí)參類型的父類類型。

下面通過 TreeSet 集合中的一個構(gòu)造方法來進(jìn)一步理解 類型通配符的下限

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類通過靜態(tài)內(nèi)部類的方式,實(shí)現(xiàn)比較的接口,在構(gòu)造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()); // 報(bào)錯// 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é)論:

通過以上的比較,我們可以看出,類型通配符的下限,只能傳遞實(shí)參類型的或者實(shí)參類型的父類類型。

我們每次比較使用的都是 Cat 類型,但在 Comparator1比較的是 Animal 中的 name 屬性,這是因?yàn)?我們在初始化 Cat 對象的時候,一定會先初始化 Animal 對象,也就是創(chuàng)建子類對象的時候,一定會先創(chuàng)建父類對象,所以才可以進(jìn)行比較。

如果是使用 類型通配符的上限,在創(chuàng)建對象時,比較的是該類的子類對象中的屬性,就會造成空指針異常!也就是Comparator3無法使用的原因, 所以在 TreeSet 中才會使用 <? super E> ,類型通配符的下限。

類型擦除

泛型是Java 1.5 引進(jìn)的概念,在這之前是沒有泛型的,但是,泛型代碼能夠很好地和之前的代碼兼容。那是因?yàn)椋盒托畔⒅淮嬖诰幾g階段,在進(jìn)入 JVM 之前,與泛型相關(guān)的信息會被擦除掉,我們稱之為——類型擦除

無限類型擦除

先定義一個泛型類:

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 類型

有限類型擦除

還是剛才的泛型類,只不過加了泛型的上限

public class Erasure<T extends Number> {// ...}

測試不變,輸出結(jié)果:

key:Number

當(dāng)我們指定了泛型的上限時,它會將我們的泛型擦除為上限類型

同樣對泛型方法,也是一樣的道理

// 泛型方法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; }}

測試

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 接口中的一個方法,但通過反射卻拿到了兩個方法。其中返回值為 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; }}泛型數(shù)組

開發(fā)中,一般常用的是泛型集合

泛型數(shù)組的創(chuàng)建:

可以聲明帶泛型的數(shù)組引用,但是不能直接創(chuàng)建帶泛型數(shù)組對象。

可以通過 java.lang.reflect.Array 的 newInstance(Class<T>, int)創(chuàng)建 T[ ] 數(shù)組。

// 可以創(chuàng)建帶泛型的數(shù)組引用ArrayList<String>[] arrayLists1 = new ArrayList[3];// 無法創(chuàng)建帶泛型的數(shù)組對象ArrayList<String>[] arrayLists2 = new ArrayList<String>[3];

簡單使用 java.lang.reflect.Array 的 newInstance(Class<T>, int)創(chuàng)建 T[ ] 數(shù)組。 封裝一個泛型數(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

通過反射創(chuàng)建對象,帶泛型和不帶泛型

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泛型的詳細(xì)內(nèi)容,更多關(guān)于JAVA泛型的資料請關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

標(biāo)簽: Java
相關(guān)文章:
主站蜘蛛池模板: 全部免费特别黄的视频播放 | 麻豆成人在线 | 香蕉视频91| 停停五月天| 亚洲美色综合天天久久综合精品 | 亚洲精品国产精品国自产 | 国产精品久久久久久久久久久威 | 最近手机中文在线视频 | 国产图片区 | 亚洲欧洲精品国产二码 | 成年男女免费视频网站 | 91 视频网站 | 国产2021中文天码字幕 | 亚洲国产精品自产拍在线播放 | 成人国产第一区在线观看 | 国产精品嫩草视频永久网址 | 免费黄色小视频网站 | 91精品福利手机国产在线 | 欧美精品三区 | 欧美成人一区二区三区不卡视频 | 亚洲精品91在线 | 国产亚洲欧洲精品 | 国产精品高清全国免费观看 | 国产精品偷伦视频免费观看的 | 免费视频国产 | 美女免费观看一区二区三区 | 一97日本道伊人久久综合影院 | 国产精品久久久久久久成人午夜 | xxxxxxhd日本d | 成人精品视频一区二区在线 | 精品外国呦系列在线观看 | 日韩中文字幕推理片 | 国内在线观看 | 欧美日韩在线高清 | 国产亚洲精品美女久久久久 | 欧美一级片在线看 | 国产日韩精品欧美一区色 | 国产在线拍偷自揄观看视频网站 | 一级国产特黄aa大片 | 精品国产欧美一区二区五十路 | 激情亚洲婷婷 |