泛型擦除和泛型数组

泛型擦除:

泛型只是在 编译期 保证对象类型相同的技术,编译后就被擦除了。真正在代码的运行期,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

泛型擦除和泛型数组_第1张图片

使用反射也可以验证 !

//无限制的类型擦除!
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

泛型擦除和泛型数组_第2张图片

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点都类似!

泛型擦除和泛型数组_第3张图片

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);
	}
}

泛型擦除和泛型数组_第4张图片

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是任何类型!
         */
        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[] lists;  // 只能声明带泛型的数组
        // lists = new ArrayList[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

你可能感兴趣的:(JAVASE进阶知识)