1. Upper bound wildcards

First of all, you need to know that arrays in the Java language are payment covariant. What does that mean? Look at the following code:

    static class A extends Base{
        void f(a) {
            System.out.println("A.f"); }}static class B extends A {
        void f(a) {
            System.out.println("B.f"); }}static class C extends B {
        void f(a) {
            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
Copy the code

We have an array of type A, but it makes sense to add B and C to it. Why? We found that the inherited A and B belong to A subclass, inherited the B, C belong to A subclass of B, in Java inheritance can be passed, so still belong to A subclass of C, B and C are A subclass of so, another thing, in Java, type up conversion is very natural, don’t need to cast will automatically, that is to say, Both instances of B and C can be automatically converted to instances of type A. Ok, so with that background, we can look at upper bound wildmatches. In Java, we can use <? Extends Type> defines an upper bound, <? Extends Type> extends Type> means all subclasses of Type. Type is an upper bound. Extends A> means that all subclasses of A can match this wildcard. All of our instances of B and C and their subclasses match, but not Base, because Base is the parent of A and our upper bound is A. Naturally, we have the following code:

        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);
Copy the code

We think it’s natural to do that, right? But the compiler obviously won’t let us do that. Why? Our list type uses an upper bound wildcard and matches all subclasses of A, while our add type matches all subclasses of A. Why not? Let’s look at <? Extends A>, our list can hold any subclass of A, i.e. instances of A, B, and C. Extend B> is considered <? What about subclasses of extends A>? <? Entends C >? Let’s assume it’s ok, but look at the following method:

    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);
    }
Copy the code

Of course, the above code will not compile, let’s analyze why, remember <? Extends A> sets an upper bound, so joke method parameters are open and we can pass in A <? The list of extend A> can also be A <? Extends B> list, which can also be a <? Extends C> list. Because the following code can be compiled:

    private static void jokeIn(List
         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);
    }
Copy the code

Okay, so here’s the problem, when we pass the argument in the joke method is List<? When extends A>, add inside A method is acceptable, but when we pass in A List<? When extends B>, list.add(a) obviously fails because our list would be allowed to hold subclasses of B, but a is not in the scope and is therefore illegal. What about extends C>? Even list.add(B) is not allowed. So that’s the problem, so it’s wise not to allow such code to compile, because we can’t always guarantee that the user who calls joke will strictly pass in a List<? Extends A> parameter. So how do we use an upper bound? In other words, how to produce a List<? What about the list of extends A>? Remember when we started talking about array covariant? The following code uses the covariant capability of Java language arrays to produce a list with an upper bound:

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

Arrays.asList(T … Data) uses one of ArrayList’s constructors:

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

As you can see, Arrays are covariant, which allows us to pass in all subclasses of A from arrays.aslist.

2. Lower wildcard

The upper bound defines that you can reach the highest point, beyond which is illegal; The lower bound is the bottom line, you can only be higher than the bottom line, below the bottom line is illegal. In Java, you can use <? Super Type> to express the meaning of the lower bound, specifically, <? Super A> expresses the sum of <? Extends A> is two opposite directions. The former extends all base classes based on A and the latter all subclasses based on A.

    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);
    }
Copy the code

The joke method takes a List<? Super A>, List<? Super < B > and List? Super C> all become List<? Super A>, because actually List<? Super < B > and List? Super C> expresses more ability than List<? Super A> is stronger, that is, List<? Super C> contains List<? Super < B > and List? Super A>, while List<? Super B> contains List<? Super A>, ok, with that said, let’s see what happens when we call the Joke method:

    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
    }
Copy the code

Well, the problem is, we can put List<? The argument to super A> is passed to Joke, because that’s what we need, and we know that List<? Super A> can be expressed in the List<? Super B > and <? Super C> is the lowest, so when we add an expression stronger than List<? The compiler reported an error after the super A> argument was passed to Joke. Of course, this is just to illustrate the definition of the so-called lower bound.

With a lower bound, we can use the following code to work for us:

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

This type is the base class of A. We just define the lower bound. As long as it is higher than this lower bound, it can be accepted by Lists.

3. Unbounded wildcards

With upper and lower bounds, and unbounded, you can’t use upper and lower bounds at the same time, because there is unbounded!! We use <? > to express unbounded, for <? > < p style = “max-width: 100%; clear: both;

I want Java generics to write this code. I don't want to use native classes here, but in this case, generic parameters can hold any type. ---- from Ideas for Java Programming15.10.3 Unbounded Wildcards (686Page)Copy the code

One scenario for using unbounded wildcards is if you use <? The > method passes a native type, so it is possible for the compiler to infer the actual parameter type, allowing this method to return and call another method that uses the exact type. This is called “type capture”, look at the following code:

    static class Holder<T> {
        private T data;

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

        public T getData(a) {
            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);
        
    }
Copy the code

As you can see, the actual argument is concrete T, while the func argument is unbounded <? > < div style = “box-sizing: border-box; color: RGB (50, 50, 50); display: inherit! Important; word-wrap: inherit! Important;” You can use unbounded wildcards to receive objects of multiple types, and then deliver them to different methods for processing depending on the type. Recall how an operating system’s interrupt handler works by installing some interrupt types and their handlers. The interrupt processing signal is then distributed to different handlers through the control program. The idea is the same. To understand the model, see the following code:

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); }Copy the code