泛型-类型通配符

类型通配符:额…说白了就是一个?。当确定集合是某种数据类型的时候,你可以写List<String>,而当不确定集合是哪一种类型的时候,就可以写成List<?>

在Java集合框架中,对于参数值是未知类型(即使用“?”通配符)的容器类,由于编译器无法预知其具体类型,所以只能读取,不能增删,但NULL是例外。

如下面的代码就会编译出错

public class Test6 {

    public static void main(String[] args) {

        List<Integer> list=new ArrayList<Integer>();
        Test6.show(list);
    }

    public static void show(List<?> list){
        list.add("1");
        list.add(null);
    }
}

list定义的是无边界通配符,往一个未知类型的l中加入类型为String的数据,编译器就会报错,但加入null就不会报错。

1.子类限定通配符:<? extends E>

public class Test4 {

    public static void c(List<? extends String> l) {
        String s = l.get(0);
        System.out.println(s);
        //l.add("ceshi");
    }

    public static void main(String[] args) {
        List<Integer> l1 = new ArrayList<Integer>();
        l1.add(1);

        List<String> l2 = new ArrayList<String>();
        l2.add("2");

        //error:The method c(List<? extends String>) in the type TestT is not applicable for the arguments (List<Integer>)
        //Test4.c(l1);
        Test4.c(l2);
    }
}

2.超类限定通配符:<? super E>
表示能够接受指定类及其父类类型的数据.<?>必须为E或者E的父类。

public class Test5 {

    public static void superD(List<? super Integer> s) {
        Object object = s.get(0);
        System.out.println(object);
        //s.add(10);
    }

    public static void main(String[] args) {
        List<String> lString = new ArrayList<String>();
        lString.add("2");

        List<Object> lObject = new ArrayList<Object>();
        lObject.add("2");

        //error:The method superD(List<? super Integer>) in the type TestT is not applicable for the arguments (List<String>)
        //Test5.superD(lString);
        Test5.superD(lObject);
    }
}

3.PECS原则
PECS全文为“Producer Extends, Consumer Super”,意思是作为作为生产者时使用extends,作为消费者时使用super。

    public static void main(String[] args) {

        List<? extends Integer> fe = new ArrayList<Integer>();
        //The method add(capture#5-of ? extends Object) in the type List<capture#5-of ? extends Object> is not applicable for the arguments (String)
        //生产者操作
        fe.add(1);
        
        List<? super Integer> fs = new ArrayList<Integer>();
        fs.add(1);

        //消费者操作
        for (Integer s : fe) {
        }
        for (Integer s : fs) {
        }
    }
}

可以看出,fe.add()作为生产者的动作往集合中添加String类型的数据时,编译器会报错:因为java中不允许不确定的类型加入集合,<?>作为Integer的子类,此时编译器并不知道fe被add进去的具体是什么子类型,因此就会报错(因为子类可能有着和父类不一样的形态,如果不限定规则,随便往里添加各种不同的子类,那在读取list里的内容时,就会出错,因为Java干脆把规则限定在前面)。而在进行消费者的操作时,fs在遍历数据时,由于数据类型<?>为Integer或者Integer的父类,因此,<?>并不知道具体是哪一种类型,所以编译器会报错。因为PECS最大的原则就是无论是生产操作还是消费操作,必须让编译器知道具体操作的类型是什么,否则不好意思,不给过!!

由上面的例子我们可以整理出一下规则:

如果要从集合中读取类型T的数据,并且不能写入,可以使用 ? extends 通配符;(Producer Extends)
如果要从集合中写入类型T的数据,并且不需要读取,可以使用 ? super 通配符;(Consumer Super)
如果既要存又要取,那么就不要使用任何通配符
在Java中,泛型是一种强类型机制,它可以让你在编译时检查类型错误,从而提高代码的安全性和可读性。在使用泛型时,我们经常会遇到父类和子类的泛型转换问题。 首先,我们需要明确一点:子类泛型不能转换成父类泛型。这是因为Java中的泛型是不协变的。例如,如果有一个类A和它的子类B,那么List<A>和List<B>之间是不存在继承关系的。 下面我们来看一个例子: ```java public class Animal { //... } public class Dog extends Animal { //... } public class Test { public static void main(String[] args) { List<Animal> list1 = new ArrayList<>(); List<Dog> list2 = new ArrayList<>(); list1 = list2; // 编译错误 } } ``` 在这个例子中,我们定义了Animal类和它的子类Dog。然后我们定义了两个List,分别是List<Animal>和List<Dog>。如果将List<Dog>赋值给List<Animal>,会出现编译错误。这是因为List<Animal>和List<Dog>之间不存在继承关系。 那么,如果我们想要让子类泛型转换成父类泛型,应该怎么办呢?这时我们可以使用通配符来解决问题。通配符可以表示任意类型,包括父类和子类。例如,我们可以将List<Dog>赋值给List<? extends Animal>,这样就可以实现子类泛型转换成父类泛型了。 下面我们来看一个使用通配符的例子: ```java public class Animal { //... } public class Dog extends Animal { //... } public class Test { public static void main(String[] args) { List<Animal> list1 = new ArrayList<>(); List<Dog> list2 = new ArrayList<>(); list1 = list2; // 编译错误 List<? extends Animal> list3 = new ArrayList<>(); list3 = list2; // 正确 } } ``` 在这个例子中,我们定义了List<? extends Animal>来表示任意继承自Animal的类型。然后我们将List<Dog>赋值给List<? extends Animal>,这样就可以实现子类泛型转换成父类泛型了。 总结一下,Java中的泛型是不协变的,子类泛型不能转换成父类泛型。如果需要实现子类泛型转换成父类泛型,可以使用通配符来解决问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

熊猫-IT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值