Java泛型中的通配符

1、上界通配符

首先,需要知道的是,Java语言中的数组是支持协变的,什么意思呢?看下面的代码:

    static class A extends Base{
        void f() {
            System.out.println("A.f");
        }
    }

    static class B extends A {
        void f() {
            System.out.println("B.f");
        }
    }

    static class C extends B {
        void f() {
            System.out.println("C.f");
        }
    }

    static {

        A[] arrayA = new A[3];

        arrayA[0] = new A();
        arrayA[1] = new B();
        arrayA[2] = new C();

        for (A item : arrayA) {
            item.f();
        }

    } 

//output
A.f
B.f
C.f

我们明明让数组的类型为A,但是向其中加入B、C也是可以行得通的,为什么呢?我们发现B继承了A,属于A的子类,C继承了B,属于B的子类,Java中的继承是可以传递的,所以C依然属于A的子类,所以B和C都是A的子类,另外一点,在Java中,类型向上转换是非常自然的,不需要强制转换会自动进行,也就是说,B和C的实例都可以自动转换为类型A的实例。好了,有了这样的背景知识,我们可以来看一下上界通配了,在java中,可以使用

        A a = new B();
        A b = new C();
        C c = new C();

       List<? extends A> list = new ArrayList<A>();

      list.add(a);
      list.add(b);
      list.add(c);

我们觉得很自然这样做是无可厚非的,对吧?但是编译器很显然不允许我们这样做,为什么?我们的list的类型使用了上界通配符啊,而且匹配的是所有A的子类,而我们add的都是A的子类啊,为什么不可以呢?我们再来看一下

    void joke(List<? extends A> list) {
        A a = new B();
        A b = new C();
        C c = new C();

        list.add(a);
        list.add(b);
        list.add(c);
    }

当然上面的代码是无法通过编译的,我们分析一下为什么,记住

    private static void jokeIn(List<?extends A> list) {
        //
    }

    static {
        List<? extends A> list = new ArrayList<>();
        List<? extends B> list1 = new ArrayList<>();
        List<? extends C> list2 = new ArrayList<>();

        jokeIn(list);
        jokeIn(list1);
        jokeIn(list2);
    }

好吧,问题来了,当我们传到joke方法中的参数是List

     List<? extends A> list = Arrays.asList(a, b);

Arrays.asList(T … data)使用了ArrayList的一个构造函数:

        ArrayList(E[] array) {
            a = Objects.requireNonNull(array);
        }

可以看到使用了数组的协变,使得我们可以在Arrays.asList里面传递进去所以A的子类对象。

2、下界通配符

上界定义了可以达到了最高点,超出就是违法的;而下界则是说明了底线,你只能比底线更高级,低于底线就是违法的。在java里面,可以使用

    void joke(List<? super A> list) {
        A a = new B();
        A b = new C();
        C c = new C();

        list.add(a);
        list.add(b);
        list.add(c);
    }

此时的joke方法的参数是List

    static {
        List<? super A> list = new ArrayList<>();
        List<? super B> list1 = new ArrayList<>();
        List<? super C> list2 = new ArrayList<>();

        jokeIn(list);
        jokeIn(list1); // error
        jokeIn(list2); //error
    }

好吧,问题出现了,我们可以将List

       List<? super A> lists = new ArrayList<>();
        lists.add(a);
        lists.add(b);
        lists.add(c);

解释一下,lists里面的元素类型是这样一种类型,这种类型是A的基类,我们只是界定了下界,只要高于这个下界,就可以被lists接收,而b、c的基类都是A,可以被lists接收,所以上面的代码是可以工作的。

3、无界通配符

有了上界和下界,还有无界,需要说明的一点是,不能同时使用上界和下界,因为有无界啊(开玩笑的)!!
我们在java中使用

我是想要java的范型来编写这段代码,我在这里并不是想使用原生类
型,但是在当前这种情况下,泛型参数可以持有任何类型。

                     ----来自《java编程思想》15.10.3 无界通配符(686页)

使用无界通配符的一种场景是:如果向一个使用

    static class Holder<T> {
        private T data;

        public Holder() {

        }

        public Holder(T data) {
            this.data =data;
        }

        public T getData() {
            return data;
        }

        public void setData(T data) {
            this.data = data;
        }
    }


    static <T> void actual(Holder<T> holder) {
        System.out.println(holder.getData());
    }

    static void func(Holder<?> holder) {
        actual(holder);
    }

    static {

        Holder<?> holder = new Holder<>("hujian");

        func(holder);

    }

可以看到,actual的参数是具体的T,而func的参数是无界的

    static <T> void actual(Holder<T> holder) {
        T data = holder.getData();
        if (data instanceof String) {
            actual((String) data);
        } else if (data instanceof Integer) {
            actual((Integer) data);
        } else if (data instanceof Double) {
            actual((Double) data);
        }
    }

    static void actual(String holder) {
        System.out.print("string:" + holder);
    }

    static void actual(Integer holder) {
        System.out.println("Integer:" + holder);
    }

    static void actual(Double holder) {
        System.out.println("double:" + holder);
    }

    static void func(Holder<?> holder) {
        actual(holder);
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值