JAVA当中的泛型

JAVA泛型其实是类型的类型,比如List数组我们可以接受任意类型的元素存储在数组当中,也可以在定义数组时,指定集合当中只存储某一类型。可以说泛型给我们的程序代码提供了可扩展性。

关于泛型的声明,如何使用,这篇暂不介绍,我们从比较容易困惑的地方开始

<? extend T> T可以是任意具体类型

举个例子如果我们定义了一个方法,接受List<Number> 类型的参数,那我们是否可以传递List<Integer>进去,答案是否定的

<? extend T> 表示的是当前泛型当中的元素,必须是T或者T类型的子类或者实现类

我们来看个例子,以下是三个类型的继承关系

class Fruit{}
class Apple extends Fruit{}
class Orange extends Fruit{}
public class Test {
    public static void main(String[] args) {
        List<Apple> apples = new ArrayList<>();
        apples.add(new Apple());
        List<Fruit> fruits = new ArrayList<>();
        fruits.add(new Fruit());
        Test.Transfer<Fruit> transfer = new Test.Transfer<Fruit>();
        Fruit f1 = transfer.transfer(apples);
        Fruit f2 = transfer.transfer(fruits);

    }
    static class Transfer<T>{
        T transfer(List<? extends T> list) {
            return list.get(0);
        }
    }
}

这里我们给出了一个静态内部类,用来转化,传入的是List对象,获取到List当中的元素,因为List当中存放的元素都是T类型的子类,所以我们在返回值上设定为T,是没有问题的

但是有个新的问题,如果我们想往List<? extends Fruit>当中添加元素时,却不能添加成功

 List<? extends Fruit> fruits = new ArrayList<Apple>();
        fruits.add(new Fruit()) //编译不通过

原因在于,当我们往集合当中添加元素时,因为集合的声明是Fruit的子类,所以我们并不清楚是具体的那个子类,Apple或者是Fruit,这个时候编译器就会提示报错了,接下来就是关于泛型的另外一个使用方式了

<? supper T>表示集合当中存放的必须是T类型或者T以上的父类型

public class Test {
    public static void main(String[] args) {
        List<Fruit> fruits = new ArrayList<>();
        List<Apple> apples = new ArrayList<>();
        Test.Transfer transfer = new Test.Transfer<Apple>();
        transfer.transfer(fruits,new Apple());
        transfer.transfer(apples, new Apple());
    }
    static class Transfer<T>{
        void transfer(List<? super T> list,T item) {
             list.add(item);
        }
    }
}

基于以上方式实现,就可以在数组当中添加不同的类型

总结 extends 和 supper 一个是用来查询集合当中的元素,另一个是用来往集合当中添加元素

类型擦除

泛型当中如果我们定义的是Node<T> 那么编制出来的T就是Object, 如果是 ? extends List 那么编译出来就是List

1、JAVA当中不允许创建泛型类型的数组

Object[] list = new String[2];
        list[0] = "abc";
        list[1] = 123;

上面这段代码我们在编译时是没有问题的,但是当执行的时候,就会抛出类型转换异常,原因是String类型的数组不能存放整形

如果我们定义的集合是List[] list = new ArrayList<String>[];

但是在存放数据时,保存的是list[0] = new ArrayList<Integer>; list[1] = new ArrayList<String>; 因为java当中存在类型擦除,所以ArrayList<String> 和ArrayList<Integer> 是一样的!为了避免这种问题发生,所以就不允许创建泛型类型的数组。

2、对于泛型代码,JAVA编译器会生成一个bridge method 

public class Node<T> {
    public T data;
    public Node(T data) {
        this.data = data;
    }

    public void setData(T data) {
        System.out.println("set Node data");
        this.data = data;
    }
    public static void main(String[] args) {
        MyNode myNode = new MyNode(5);
        Node node = myNode;
        node.setData("adb"); //抛出异常
    }
}
class MyNode extends Node<Integer>{
    public MyNode(Integer data) {
        super(data);
    }
    public void setData(Integer data) {
        System.out.println("set myNode data");
        super.data = data;
    }
}

上面这段代码在编译时是可以的,但是运行时就会抛出异常错误,原因在于在MyNode方法当中编译时会生成一个BridgeMethod

class MyNode extends Node {
    // Bridge method generated by the compiler
    public void setData(Object data) {
        setData((Integer) data);
    }
    public void setData(Integer data) {
        System.out.println("MyNode.setData");
        super.setData(data);
    }
    // ...
}

这就是为什么不能转化String为Integer的原因

3、关于泛型使用instanceOf

我们无法对泛型代码直接使用instanceof ArrayList<Integer> 因为对编译器来说ArrayList<Integer>和ArrayList<String>是同一个类型,如果我们想判断是否为ArrayList的话,可以通过这种方式来调用

public static void rtti(List<?> list) {
    if (list instanceof ArrayList<?>) {  // OK; instanceof requires a reifiable type
        // ...
    }
}
以上就是关于泛型的一些内容,如有问题欢迎指正

猜你喜欢

转载自blog.csdn.net/summerzbh123/article/details/80970570