Thinking logic of computer programs (36) and thinking 36

Source: Internet
Author: User
Tags addall comparable

Thinking logic of computer programs (36) and thinking 36

In the previous section, we introduced the basic concepts and principles of generics. In this section, we will continue to discuss generics and the wildcard concepts in generics. Wildcards have confusing and confusing syntaxes, but they are widely used in Java container classes. What is it? In this section, let's analyze it gradually.

More concise parameter type restrictions

In the last section, we mentioned an example. To add an Integer object to the Number container, another type parameter is used as the upper bound of the type parameter. The Code is as follows:

public <T extends E> void addAll(DynamicArray<T> c) {    for(int i=0; i<c.size; i++){        add(c.get(i));    }}

We mentioned that this writing method is a bit cool and can be replaced with a more concise wildcard form:

public void addAll(DynamicArray<? extends E> c) {    for(int i=0; i<c.size; i++){        add(c.get(i));    }}

This method does not define the type parameter. The type of c is DynamicArray <? Extends E> ,? Represents a wildcard, <? Extends E> indicates a qualified wildcard that matches a child type of E or E. We do not know the specific child type.

The code used in this method does not need to be modified. It can also be:

DynamicArray<Number> numbers = new DynamicArray<>();DynamicArray<Integer> ints = new DynamicArray<>();ints.add(100);ints.add(34);numbers.addAll(ints);

Here, E is the Number type, DynamicArray <? Extends E> can match DynamicArray <Integer>.

<T extends E> and <? Extends E>

So the question is: The same extends keyword, also applied to generics, <T extends E> and <? Extends E> what is the relationship?

They are used in different places. Let's explain:

  • <T extends E> defines a type parameter. It declares a type parameter T, which can be placed behind the class name in the generic class definition and before the return value of the generic method.
  • <? Extends E> is used to instantiate type parameters. It is used to instantiate type parameters in generic variables, but the specific type is unknown and only knows that it is a subtype of E or E.

Although they are different, the two writing methods can often achieve the same goal. For example, in the previous example, the following two writing methods can be used:

public void addAll(DynamicArray<? extends E> c)public <T extends E> void addAll(DynamicArray<T> c) 

So what form should we use? We need to further understand the wildcard before explaining it.

Understanding wildcards

Unlimited wildcard

There is also a wildcard, such as DynamicArray <?>, It is called an infinite set wildcard. Let's look at an example of using DynamicArray to find the specified element. The Code is as follows:

public static int indexOf(DynamicArray<?> arr, Object elm){    for(int i=0; i<arr.size(); i++){        if(arr.get(i).equals(elm)){            return i;        }    }    return -1;}

In fact, the type parameter can also be used in the form of an infinite set wildcard. That is to say, the following statement is written:

public static int indexOf(DynamicArray<?> arr, Object elm)

You can change it:

public static <T> int indexOf(DynamicArray<T> arr, Object elm)

However, the wildcard format is more concise.

Read-only wildcard characters

The wildcard format is more concise, but both of the above two wildcards have an important restriction. They can only be read and cannot be written.

How can this problem be solved? Take the following example:

DynamicArray<Integer> ints = new DynamicArray<>();DynamicArray<? extends Number> numbers = ints;Integer a = 200;numbers.add(a);numbers.add((Number)a);numbers.add((Object)a);

The three add methods are invalid. The Compiler reports an error whether it is an Integer, Number, or Object. Why?

? It indicates type security ignorance ,? Extends Number indicates a subtype of Number, but does not know the specific subtype. If writing is allowed, Java cannot ensure the type security, so it is simply forbidden. Let's look at an example to see what will happen if write is allowed:

DynamicArray<Integer> ints = new DynamicArray<>();DynamicArray<? extends Number> numbers = ints;Number n = new Double(23.0);Object o = new String("hello world");numbers.add(n);numbers.add(o);

If the write Object or Number type is allowed, the last two lines of compilation are correct. That is to say, Java will allow the Double or String Object to be placed in the Integer container, this apparently violates Java's commitment to type security.

In most cases, this restriction is good, but it makes some basic operations that are supposed to be correct unable to be completed, such as exchanging the positions of two elements and viewing the Code:

public static void swap(DynamicArray<?> arr, int i, int j){    Object tmp = arr.get(i);    arr.set(i, arr.get(j));    arr.set(j, tmp);}

This Code seems to be correct, but Java will prompt a compilation error. The two rows of set statements are invalid. However, using the generic method with type parameters, this problem can be solved as follows:

private static <T> void swapInternal(DynamicArray<T> arr, int i, int j){    T tmp = arr.get(i);    arr.set(i, arr.get(j));    arr.set(j, tmp);}public static void swap(DynamicArray<?> arr, int i, int j){    swapInternal(arr, i, j);}

Swap can call swapInternal, while swapInternal with type parameters can be written. The Java container class has similar usage. The common API is in the wildcard format, and the form is simpler. However, the method with type parameters is called internally.

Dependencies between parameter types

In addition to this case, if there is a dependency between parameter types, you can only use type parameters. For example, you can copy the content in the src container to the dest using the following code:

public static <D,S extends D> void copy(DynamicArray<D> dest,        DynamicArray<S> src){    for(int i=0; i<src.size(); i++){        dest.add(src.get(i));    }}

S and D are dependent, either the same, or S is a subclass of D. Otherwise, the type is incompatible and there is a compilation error. However, the preceding Declaration can be simplified by using wildcards. The two parameters can be simplified to one, as shown below:

public static <D> void copy(DynamicArray<D> dest,        DynamicArray<? extends D> src){    for(int i=0; i<src.size(); i++){        dest.add(src.get(i));    }}

Wildcards and returned values

Also, if the return value depends on the type parameter, wildcards cannot be used. For example, the maximum value in a dynamic array is calculated as follows:

public static <T extends Comparable<T>> T max(DynamicArray<T> arr){    T max = arr.get(0);    for(int i=1; i<arr.size(); i++){        if(arr.get(i).compareTo(max)>0){            max = arr.get(i);        }    }    return max;}

The above code is difficult to replace with wildcards.

Wildcard or type parameter?

Now let's take a look at whether generic methods should use wildcards or add type parameters? What is the relationship between the two? Below is a summary:

  • The wildcard format can be replaced by the type parameter. The wildcard can do anything but the type parameter.
  • The wildcard form can reduce the type parameter, which is simpler in form and better in readability. Therefore, wildcards can be used for wildcard.
  • If there is a dependency between the type parameters, or the return value depends on the type parameters, or you need to write the operation, you can only use the type parameters.
  • Wildcard form and type parameters are often used together. For example, the copy method above defines necessary type parameters, uses wildcards to express dependencies, and accepts a wider range of data types.

Super-type wildcard

Flexible writing

There is also a wildcard with the form <? Extends E> is the opposite. Its form is <? Super E>, called a super-type wildcard, indicates a parent type of E. What is its use? With it, we can write more flexibly.

If there is no such syntax, there are some restrictions on writing. Let's look at an example. We will add a method to DynamicArray:

public void copyTo(DynamicArray<E> dest){    for(int i=0; i<size; i++){        dest.add(get(i));    }}

This method is also very simple. Add the elements in the current container to the input target container. We may want to use this method:

DynamicArray<Integer> ints = new DynamicArray<Integer>();ints.add(100);ints.add(34);DynamicArray<Number> numbers = new DynamicArray<Number>();ints.copyTo(numbers);

Integer is a subclass of Number. It is reasonable to copy the Integer object into the Number container. However, Java prompts a compilation error for the reason we have mentioned before, the expected parameter type is DynamicArray <Integer>, and DynamicArray <Number> does not apply.

As mentioned previously, DynamicArray <Integer> cannot be considered as a DynamicArray <Number>, but the usage here is correct. The method for Java to solve this problem is ultra-type wildcards, you can change the copyTo code:

public void copyTo(DynamicArray<? super E> dest){    for(int i=0; i<size; i++){        dest.add(get(i));    }}

In this way, there is no problem.

Flexible comparison

The Comparable/Comparator interface is another common case for wildcard wildcards. Similarly, let's take a look at the restrictions if we don't use them. For example, the method used to calculate the maximum value is declared as follows:

public static <T extends Comparable<T>> T max(DynamicArray<T> arr)

What are the restrictions on this statement? Let's take a simple example. There are two classes: Base and Child. The Code of Base is:

class Base implements Comparable<Base>{    private int sortOrder;        public Base(int sortOrder) {        this.sortOrder = sortOrder;    }        @Override    public int compareTo(Base o) {        if(sortOrder < o.sortOrder){            return -1;        }else if(sortOrder > o.sortOrder){            return 1;        }else{            return 0;        }    }}

The Base code is very simple. It implements the Comparable interface and compares it according to the instance variable sortOrder. The Child code is:

class Child extends Base {    public Child(int sortOrder) {        super(sortOrder);    }}

Here, Child is very simple, but inherits the Base. Note that Child does not re-implement the Comparable interface because the comparison rules of Child are the same as those of Base. We may want to use the previous max method to operate the Child container, as shown below:

DynamicArray<Child> childs = new DynamicArray<Child>();childs.add(new Child(20));childs.add(new Child(80));Child maxChild = max(childs);

Unfortunately, a compilation error is prompted in Java, and the type does not match. Why not? We may think that Java will deduce the type parameter T of the max method as the Child type, but the requirement of the type T is extends Comparable <T>, child does not implement Comparable <Child>. It implements Comparable <Base>.

However, our requirements are reasonable. The code of the Base class already has all the data required for comparison. It should be used to compare Child objects. To solve this problem, modify the max method declaration and use a wildcard character, as shown below:

public static <T extends Comparable<? super T>> T max(DynamicArray<T> arr)

Just modify it. This method is abstract. replace T with Child, that is:

Child extends Comparable<? super Child>

<? Super Child> can match the Base, so the whole is matched.

No <T super E>

We will compare the type parameter limitation with the super-type wildcard. The type parameter limitation is only in the extends form, and there is no super form. For example, in the previous copyTo method, its wildcard form declaration is:

public void copyTo(DynamicArray<? super E> dest)

If the type parameter supports the super format, it should be:

public <T super E> void copyTo(DynamicArray<T> dest)

The fact is that Java does not support this syntax.

As we have said before, for a qualified wildcard form <? Extends E>, which can be replaced by the type parameter limitation. However, for a wildcard similar to the above, the type parameter cannot be used.

Wildcard character comparison

Two wildcard forms <? Super E> and <? Extends E> is also easy to confuse. Let's make a comparison.

  • They aim to make method interfaces more flexible and accept a wider range of types.
  • <? Super E> it is used for flexible writing or comparison, so that the object can be written to the parent-type container, so that the parent-type comparison method can be applied to subclass objects.
  • <? Extends E> is used for flexible reading so that the method can read any sub-type container objects of E or E.

Java container class has many such usage. For example, Collections has the following methods:

public static <T extends Comparable<? super T>> void sort(List<T> list)public static <T> void sort(List<T> list, Comparator<? super T> c)public static <T> void copy(List<? super T> dest, List<? extends T> src)public static <T> T max(Collection<? extends T> coll, Comparator<? super T> comp)

Through the above section and this section, we should be able to understand the meaning of these method declarations.

Summary

This section describes three wildcard forms in the generic model. <?> , <? Extends E> and <? Super E>, and analyzes the difference and connection with the type parameter form.

To sum up:

  • <?> And <? Extends E> is used for more flexible reading. They can be replaced by type parameters, but the wildcard form is more concise.
  • <? Super E> is used for more flexible writing and comparison, and cannot be replaced by type parameters.

There are still some details and restrictions on generics. Let's continue to explore them in the next section.

----------------

For more information, see the latest article. Please pay attention to the Public Account "lauma says programming" (scan the QR code below), from entry to advanced, ma and you explore the essence of Java programming and computer technology. Write with your heart, original articles, and retain all copyrights.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.