欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

泛型擦除和泛型数组

程序员文章站 2022-07-15 10:17:11
...

泛型擦除:

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

泛型擦除和泛型数组

使用反射也可以验证 !

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

泛型擦除和泛型数组

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

泛型擦除和泛型数组

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

泛型擦除和泛型数组

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

相关标签: JAVASE进阶知识