Look at the following section of code
number num = new Integer (1); arraylist<number> list = new arraylist<integer> (); //type mismatchlist<? extends number> list = new arraylist<number> ( ); list. add (new Integer (1)); //errorlist.new Float (1. 2f)); //error
Some people wonder why Number
an object can be Integer
instantiated, but ArrayList<Number>
an object cannot be ArrayList<Integer>
instantiated? The list <? extends Number>
declares that its element is a derived class of number or number, why can't add Integer
and Float
? To solve these problems, we need to understand the inversion and covariance in Java and the use of wildcard characters in generics.
1. Contravariance and covariance
Prior to introducing contravariance and covariance, first introduced Liskov替换原则
(Liskov Substitution Principle, LSP).
Liskov substitution principle
The LSP was proposed by Barbara Liskov in 1987 and is defined as follows:
All references to the base class (the parent class) must be able to use the object of its subclass transparently.
The LSP contains the following four-layer meanings:
- Subclasses fully own the methods of the parent class, and the concrete subclasses must implement the abstract methods of the parent class.
- You can add your own method to the subclass.
- When a subclass overrides or implements a method of the parent class, the method's formal parameters are more lenient than the parent method.
- When a subclass overrides or implements a method of a parent class, the return value of the method is stricter than the parent class.
The previous two meanings are better understood, and the latter two meanings are explained in detail below. According to the LSP, when we instantiate an object, we can instantiate it with its subclasses, such as:
new Integer(1);
Defined
The inverse and covariance are used to describe the inheritance relationship after type transformation, which defines: if A,B represents a type,F(-) represents a type conversion, ≤ denotes an inheritance relationship (for example,A? ≤?). b means a is a subclass derived from b );
- F (contravariant) is the inverse variable, when A? ≤? b There is F(b) ≤f(A) established;
- F is covariant (covariant), when A? ≤? b There is an F(A) ≤f(b) to stand ;
- F is invariant (invariant), when A? ≤? B Neither of the two formulas is established, i.e. F(A) and F(b) have no inheritance relationship with each other.
Type conversions
Next, let's look at the covariance, contravariance, or invariance of common type conversions in Java.
Generic type
f(A)=ArrayList<A>
So, is the inverse, covariance, or invariant in F? If it is contravariant, the ArrayList<Integer>
parent type, or the subtype if it is ArrayList<Number>
covariant, ArrayList<Integer>
ArrayList<Number>
and if it is not, the two do not inherit from each other. An instantiated object error in the opening code indicates that the ArrayList<Integer>
list
generic is immutable.
Array
f(A)=[]A
, it is easy to prove that the array is covariant:
new Integer[3];
Method
The parameter of the method is covariant and the return value is contravariant:
Through discussions with netizens Iamzhoug37, update the following.
Call Method result = Method (n)
; According to the Liskov substitution principle, the type of incoming parameter n should be a subtype of the method parameter, i.e. typeof (N) ≤typeof (method ' s parameter)
; result should be the base type of the method return value, that is, typeof (Methods ' return) ≤typeof (result)
:
static number methodreturn 1;} Object result = method (new Integer (2 )); //correctnumber result = method (< Span class= "Hljs-keyword" >new Object ()); //errorinteger result = method (< Span class= "Hljs-keyword" >new Integer (2)); //error
In Java 1.4, when a subclass overrides (override) The parent class method, the type of the form that participates in the return value must be consistent with the parent class:
Class super {number < Span class= "hljs-function" >method (number N) { ...}} class sub extends super { @Override Span class= "hljs-function" >number method< Span class= "hljs-function" > (number N) { ...}}
Starting with Java 1.5, subclasses allow covariance to return more specific types when overriding a parent class method:
Class super {number < Span class= "hljs-function" >method (number N) { ...}} class sub extends super { @Override Span class= "hljs-function" >integer method< Span class= "hljs-function" > (number N) { ...}}
2. Wildcard characters in generics implement covariance and contravariance of generics
Generics in Java are immutable, and sometimes you need to implement contravariance and covariance. At this point, wildcards come ?
in handy:
<? extends>
The covariance of generics is realized, for example:
extends Number> list = new ArrayList<Integer>();
<? super>
The inversion of generics is realized, for example:
super Number> list = new ArrayList<Object>();
Extends and super
Why is List<? extends Number> list
there a Integer
compile error (in the opening code) in Add Float
? First, let's look at the implementation of add:
public interface list<e> extends collection< e> {boolean add (e e);
When the Add method is called, the generic is automatically changed to E
<? extends Number>
represent the type that the list holds 在Number与Number派生子类中的某一类型
, which contains an integer type but is not specified as an integer type (integer like a spare tire!!!). ), a Integer
compile error occurs when Add.
To be able to invoke the Add method, you can implement it with the super
keyword:
super Number> list = new ArrayList<Object>();list.add(new Integer(1));list.add(new Float(1.2f));
<? super Number>
Indicates that the list holds a type of 在Number与Number的基类中的某一类型
, where integer and float must be a 某一类型
subclass of this, so the Add method can be called correctly. As can be seen from the above example, the extends
upper bounds of the generic are determined, and the lower super
bounds of the generics are determined.
PECS
Now the question is: When do you use extends to use super? "Effective Java" gives the answer:
Pecs:producer-extends, Consumer-super.
For example, a simple stack API:
PublicClass stack<e>{Public Stack();Public void push (E E): public E pop (); public span class= "DT" >boolean isempty ();}
To implement the pushAll(Iterable<E> src)
method, put the SRC elements into the stack:
public void pushAll(Iterable<E> src){ for(E e : src) push(e)}
The
assumes that there is an instantiation stack<number>
object stack,src has iterable<integer>
and Iterable<float>
; The type mismatch error occurs when the Pushall method is called because generics in Java are immutable, iterable<integer>
and Iterable<float>
are not iterable<number>
subtypes. Therefore, you should change to
// Wildcard type for parameter that serves as an E producerpublic void pushAll(Iterable<? extends E> src) { for (E e : src) push(e);}
To implement a popAll(Collection<E> dst)
method, remove the elements in the stack in turn from add to DST if you do not use the wildcard character implementation:
//popAll Method without wildcard type-deficient! public span class= "DT" >void popall (collection<e> DST) {while (!add (pop ());}
Similarly, suppose you have an instantiated Stack<number>
object STACK,DST is collection<object>
A type mismatch error occurs when calling the Popall method because Collection<object>
is not collection<number>
Sub-type. Therefore, it should read:
//Wildcard Type for parameter, serves as an E consumer public < Span class= "Hljs-keyword" >void popall (COLLECTION<? super e> DST) {while (!< Span class= "Fu" >isempty ()) DST. add (pop ());}
In the above example, an E instance (produces e instances) is produced when the Pushall method is called, and DST consumes the E instance (consumes e instances) when the Popall method is called. Naftalin and Wadler will pecs called Get and Put Principle.
Java.util.Collections's Copy method (JDK1.7) perfectly interprets the pecs:
PublicStatic <T>void Copy(LIST<?SuperT> dest, list<?Extendst> src) {int srcsize = src.Size ();if (Srcsize > Dest.Size ())ThrowNew Indexoutofboundsexception ("Source does not fit in dest");if (Srcsize < Copy_threshold | | (srcinstanceof randomaccess && dest instanceof randomaccess) {for ( int i=0; i<srcsize; i++) dest. set (i, Src.else {listiterator<? super t> di=dest.extends t> si=src. listiterator (); for (int i= 0; i<srcsize; i++) {di. next (); Di. set (Si.
Pecs Summary:
- To fetch data from a generic class, use extends;
- To write data to a generic class, use super;
- Both take and write, without wildcards (that is, extends and super are not necessary).
3. References
[1] Meriton, covariance, invariance and Contravariance explained in plain 中文版?.
[2] Bert F, difference between <? Super t> and <? Extends t> in Java.
[3] Joshua Bloch, effective Java.
Http://www.cnblogs.com/en-heng/p/5041124.html
Inversion and covariance in Java (RPM)