泛型擦除和泛型數組

泛型擦除:

泛型只是在 編譯期 保證對象類型相同的技術,編譯後就被擦除了。真正在代碼的運行期,jvm會擦除泛型的存在。(所以也可以不反編譯,使用反射來驗證泛型擦除!

  • 泛型擦除
    • 無限制類型擦除
    • 有限制類型擦除
    • 泛型方法的類型擦除
    • 橋接方法
1、無限制類型擦除
//無限制類型擦除
public class Orange<T>{
    private  T color;

    public void setColor(T color) {
        this.color = color;
    }

    public T getColor() {
        return color;
    }
}

反編譯後

public class Orange
{

	private Object color;

	public Orange(){}

	public void setColor(Object color)
	{
		this.color = color;
	}

	public Object getColor()
	{
		return color;
	}
}

注:類型形參T都變成了Object

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-f0HCooio-1592723082971)(C:\Users\wzj\AppData\Roaming\Typora\typora-user-images\1592659926154.png)]

使用反射也可以驗證 !

//無限制的類型擦除!
public class TypeErasure<T> {
    private T name;

    public T getName(){
        return this.name;
    }

    public void setName(T name){
        this.name = name;
    }

    public static void main(String[] args) {
        TypeErasure<Integer> te = new TypeErasure<>();
        TypeErasure<String> te2 = new TypeErasure<>();

        System.out.println(te.getClass().getSimpleName());
        System.out.println(te2.getClass().getSimpleName());
        System.out.println(te.getClass() == te2.getClass()); //屬於同一個類!
        //利用反射獲取字節碼文件,看成員變量的類型!

        Class<TypeErasure> clazz = TypeErasure.class;
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field field : declaredFields) {
            // Name:name	Type:Object
            System.out.println("Name:"+field.getName()+"\tType:"+field.getType().getSimpleName());
        }

    }
}
2、有限制類型擦除
// 有限制類型擦除
public class Watermelon<T extends Number>{
    private  T weight;

    public T getWeight() {
        return weight;
    }

    public void setWeight(T weight) {
        this.weight = weight;
    }
}

反編譯後

public class Watermelon
{

	private Number weight;

	public Watermelon()
	{
	}

	public Number getWeight()
	{
		return weight;
	}

	public void setWeight(Number weight)
	{
		this.weight = weight;
	}
}

注:類型形參T都變成了上限Number

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-G5WIh9nc-1592723082973)(C:\Users\wzj\AppData\Roaming\Typora\typora-user-images\1592660043426.png)]

3、泛型方法的類型擦除
// 泛型方法的類型擦除
public class Peach {

    public static <T> T getName(T name){
        return name;
    }

    public static <T extends Number> T getWeight(T weight){
        return weight;
    }
}

反編譯後

public class Peach
{

	public Peach(){}

	public static Object getName(Object name)
	{
		return name;
	}

	public static Number getWeight(Number weight)
	{
		return weight;
	}
}

注:其實本質上和上面1、2點都類似!

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-CBKc6o0l-1592723082974)(C:\Users\wzj\AppData\Roaming\Typora\typora-user-images\1592660084752.png)]

4、橋接
// 泛型接口
public interface Info<T>{
    T info(T var);
}

class InfoImple implements Info<Integer>{
    @Override
    public Integer info(Integer var) {
        return var;
    }
}

反編譯後

public interface Info
{
	// 無限制的類型擦除
	public abstract Object info(Object obj);
}

class InfoImple implements Info
{

	InfoImple(){}

	public Integer info(Integer var)
	{
		return var;
	}

	public volatile Object info(Object obj)
	{
		return info((Integer)obj);
	}
}

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-rmiRUEf8-1592723082975)(C:\Users\wzj\AppData\Roaming\Typora\typora-user-images\1592659887857.png)]

5、補充
public class Test {
    public static void main(String[] args) {
        List<Integer> intList = new ArrayList<>(Arrays.asList(1,2));
        /**
         * 1.當一個具有泛型信息的對象賦給另一個沒有泛型信息的變量時,編譯器就會丟失前者的泛型信息,這時典型的擦除
         * 例子:這裏intList就丟失了泛型的信息!
         *
         * 2.java中直接允許將List對象賦給一個List<Type>, Type是任何類型!
         */
        List list = intList;

        List<String> strList = list;
        System.out.println(strList.get(0));//java.lang.ClassCastException
        //System.out.println((String)strList.get(0)); 反編譯後上一句是這樣的!!
    }
}

泛型數組

泛型數組的創建:
1.可以聲明帶泛型的數組引用。但是不能直接創建帶泛型的數組對象!
(特例:java允許創建無上限的通配符泛型數組)
2.可以通過java.lang.reflect包下的Array.newInstance(Class, int) 創建T[]數組

注:開發中儘量使用泛型集合來代替泛型數組

1、不能直接創建帶泛型的數組對象
public class Demo {
    public static void main(String[] args) {
        // List<String>[] lists;  // 只能聲明帶泛型的數組
        // lists = new ArrayList<String>[5]; //(編譯報錯)不能直接創建帶泛型的數組

        // 如果真想創建泛型數組,可以這樣創建(new的時候不帶泛型),但是會出現編譯警告!,也就是說可能發生ClassCastException
        List<String>[] lists = new ArrayList[5];
        // 強轉成Object[]數組
        Object[] objs = (Object[])lists;

        List<Integer> intList = new ArrayList<>();
        intList.add(20);
        objs[0] = intList;

        //下面代碼引起ClassCastException異常
        String s = lists[0].get(0);
        System.out.println(s); // java.lang.ClassCastException
    }
}
  • 創建無上限的通配符泛型數組
public class Demo2 {
    public static void main(String[] args) {

        // java允許創建無上限的通配符泛型數組
        List<?>[] genericArray = new ArrayList<?>[10];
        Object[] objs = (Object[])genericArray;
        List<String> intList = new ArrayList<>(Arrays.asList("10"));
        objs[0] = intList;

        //強轉前應該instanceof判斷下
        Object target = genericArray[0].get(0);
        if(target instanceof  String){
            String s = (String)target;
            System.out.println(s+"\t"+s.getClass().getSimpleName());
        }
    }
}
  • 不能直接對類型參數T實例化
public <T> T[] makeArray(Collection<T> c){
        return new T[c.size()]; //導致編譯錯誤, 類型參數T不能直接被實例化
}
2、通過Array.newInstance()來創建泛型數組
package cn.itcast.genericType;

import java.lang.reflect.Array;
import java.util.Arrays;

public class Fruit<T> {

    // private T[] array = new T[3]; // 參數類型T不能直接被實例化!

    private T[] array;

    public Fruit(Class<T> clazz, int length){
        // 通過Array.newInstance()創建一個泛型數組
        array = (T[])Array.newInstance(clazz, length);
    }

    // 1.往指定位置填充元素
    public void put(int index, T item){
        array[index] = item;
    }

    // 2.獲取指定位置的元素
    public T get(int index){
        return array[index];
    }

    // 3.獲取數組
    public T[] getArray(){
        return this.array;
    }

    public static void main(String[] args) {
        Fruit<String> fruit = new Fruit(String.class,3);
        fruit.put(0,"蘋果");
        fruit.put(1,"香蕉");
        fruit.put(2,"橘子");
        System.out.println(Arrays.toString(fruit.getArray())); //[蘋果, 香蕉, 橘子]
    }
}

來自:雖然帥,但是菜的cxy
peace

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章