Look at the Generic Design of the Effective Java Society--Upper and Lower Limits

Keywords: Java

For the upper and lower bounds of generic wildcards in Java, at first I was a bit confused!
As a code porter, simply record the process of moving bricks, snickering.

Upper bound

The question mark <? Extends E > represents an unknown type, which is a subtype of E, including E itself, and calls E the upper bound of wildcards?

Lower bound

The unknown type must be the parent of T or T itself, and T is the lower bound of wildcard?

Upper side dishes

The code is clear at a glance:

import java.util.ArrayList;
import java.util.Collection;

class Animal {
}

class Dog extends Animal {
}

class Cat extends Animal {

}
public class VideoTutorialGeneric {
    public static void main(String[] args) {

        Collection<Object> c1 = new ArrayList<>();

        // Wildcard? Represents any type  
        Collection<?> c2 = new ArrayList<>();

        // Animal is the upper limit for compilation errors using Object 
//         Collection<? extends Animal> c9 = new ArrayList<Object>();
        Collection<? extends Animal> c3= new ArrayList<Animal>();
        Collection<? extends Animal> c4 = new ArrayList<Dog>();
        Collection<? extends Animal> c5 = new ArrayList<Cat>();

        // Animal is a lower bound using Dog Cat, which is a subtype of Animal compiled without passing  
        // Collection<? super Animal> c15 = new ArrayList<Dog>();
        // Collection<? super Animal> c16 = new ArrayList<Cat>();
        Collection<? super Animal> c6 = new ArrayList<Object>();
        Collection<? super Animal> c7 = new ArrayList<Animal>();
    }
}

Main course

Excerpt a piece of code in Effective Java to simulate data structure stack operation:

public class UpperLowerBoundGenericDemo<E> {
    private Object[] elements;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;


    public UpperLowerBoundGenericDemo() {
        elements = new Object[DEFAULT_INITIAL_CAPACITY];
    }

    public void push(E e) {
        ensureCapacity();
        elements[size++] = e;
    }

    public E pop() {
        if (size == 0)
            throw new EmptyStackException();

        @SuppressWarnings("unchecked")
        E result = (E) elements[--size];
        elements[size] = null; // Eliminate obsolete reference
        return result;
    }

    public boolean isEmpty() {
        return size == 0;
    }

    private void ensureCapacity() {
        if (elements.length == size)
            elements = Arrays.copyOf(elements, 2 * size + 1);
    }
Upper limit

Now add a method to Upper Lower Bound GenericDemo.

       public void pushAll1(Collection<E> src) {
        for (E e : src) {
            push(e);
        }
    }

Testing in main method:

   public static void main(String[] args) {
        UpperLowerBoundGenericDemo<Number> stack = new UpperLowerBoundGenericDemo<>();
        List<Integer> list = Arrays.asList(12, 4, 4);
       stack.pushAll1(list);
    }

Compilation errors will occur when pushAll1 method is run. E in pushAll1 (Collection < E > src) is Number type. Passing List < Integer > into pushAll1 is not passable when compiler detects. Because generic parameters correspond to the specific type of Number, only Number type can be passed into pushAll1 method.

Then use the upper limit of wildcards to solve this problem:

    public void pushAll(Collection<? extends E> src) {
        for (E e : src) {
            push(e);
        }
    }

Replacing pushAll1 method with pushAll method can compile and run normally. E is Number type, so unknown type? It can be subclasses of Number type such as Integer and Double.

lower limit

Add a method to Upper Lower Bound GenericDemo.

    // The function of this method is to put elements in the stack into the dst set 
    public void popAll1(Collection<E> dst) {
        while (!isEmpty()) {
            dst.add(pop());
        }
    }

Execute in main:

  public static void main(String[] args) {
        UpperLowerBoundGenericDemo<Number> stack = new UpperLowerBoundGenericDemo<>();
        List<Integer> list = Arrays.asList(12, 4, 4);

        List<Object> objects = new ArrayList<>();
        stack.popAll(objects);
    }

Compilation errors, as above, generics are specific. As soon as the compiler detects that the generic type Object and Number are inconsistent, it throws an error.

Simply modify this method:

    public void popAll(Collection<? super E> dst) {
        while (!isEmpty()) {
            dst.add(pop());
        }
    }

Then the main method is replaced by popAll to call, and the compiler runs normally.
The <? Super E > in the method declaration, in this case, E represents Number type,? Represents Object type, and Object is also the parent type of Number. For this method, it is perfect to use List < Object > to receive different types of elements in the stack.

An afternoon tea

About Comparable:

    public static <T extends Comparable<T>> T max1(List<T> list) {
        Iterator<T> i = list.iterator();
        T result = i.next();
        while (i.hasNext()) {
            T t = i.next();
            if (t.compareTo(result) > 0)
                result = t;
        }
        return result;
    }

See < T extends Comparable < T > is also a moment of confusion, what hell!
This means that T in List < T > should implement the Comparable interface. If T does not implement the Comparable interface, it is impossible to call the max1 method.

Here we go again.

   public static <T extends Comparable<? super T>> T max(List<? extends T> list) {
        // some subtype of T
        Iterator<? extends T> i = list.iterator();
        T result = i.next();
        while (i.hasNext()) {
            T t = i.next();
            if (t.compareTo(result) > 0)
                result = t;
        }
        return result;
    }

See < T extends Comparable <? Super T > I am confused, first mark, follow-up on details!

Posted by zurron on Tue, 16 Apr 2019 23:12:33 -0700