Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <blockquote> <p>Some say it is about relationship between types and subtypes, other say it is about type conversion and others say it is used to decide whether a method is overwritten or overloaded.</p> </blockquote> <p>All of the above.</p> <p>At heart, these terms describe how the subtype relation is affected by type transformations. That is, if <code>A</code> and <code>B</code> are types, <code>f</code> is a type transformation, and ≤ the subtype relation (i.e. <code>A ≤ B</code> means that <code>A</code> is a subtype of <code>B</code>), we have</p> <ul> <li><code>f</code> is covariant if <code>A ≤ B</code> implies that <code>f(A) ≤ f(B)</code></li> <li><code>f</code> is contravariant if <code>A ≤ B</code> implies that <code>f(B) ≤ f(A)</code></li> <li><code>f</code> is invariant if neither of the above holds</li> </ul> <p>Let's consider an example. Let <code>f(A) = List&lt;A&gt;</code> where <code>List</code> is declared by</p> <pre><code>class List&lt;T&gt; { ... } </code></pre> <p>Is <code>f</code> covariant, contravariant, or invariant? Covariant would mean that a <code>List&lt;String&gt;</code> is a subtype of <code>List&lt;Object&gt;</code>, contravariant that a <code>List&lt;Object&gt;</code> is a subtype of <code>List&lt;String&gt;</code> and invariant that neither is a subtype of the other, i.e. <code>List&lt;String&gt;</code> and <code>List&lt;Object&gt;</code> are inconvertible types. In Java, the latter is true, we say (somewhat informally) that <em>generics</em> are invariant.</p> <p>Another example. Let <code>f(A) = A[]</code>. Is <code>f</code> covariant, contravariant, or invariant? That is, is String[] a subtype of Object[], Object[] a subtype of String[], or is neither a subtype of the other? (Answer: In Java, arrays are covariant)</p> <p>This was still rather abstract. To make it more concrete, let's look at which operations in Java are defined in terms of the subtype relation. The simplest example is assignment. The statement </p> <pre><code>x = y; </code></pre> <p>will compile only if <code>typeof(y) ≤ typeof(x)</code>. That is, we have just learned that the statements</p> <pre><code>ArrayList&lt;String&gt; strings = new ArrayList&lt;Object&gt;(); ArrayList&lt;Object&gt; objects = new ArrayList&lt;String&gt;(); </code></pre> <p>will not compile in Java, but</p> <pre><code>Object[] objects = new String[1]; </code></pre> <p>will.</p> <p>Another example where the subtype relation matters is a method invocation expression:</p> <pre><code>result = method(a); </code></pre> <p>Informally speaking, this statement is evaluated by assigning the value of <code>a</code> to the method's first parameter, then executing the body of the method, and then assigning the methods return value to <code>result</code>. Like the plain assignment in the last example, the "right hand side" must be a subtype of the "left hand side", i.e. this statement can only be valid if <code>typeof(a) ≤ typeof(parameter(method))</code> and <code>returntype(method) ≤ typeof(result)</code>. That is, if method is declared by:</p> <pre><code>Number[] method(ArrayList&lt;Number&gt; list) { ... } </code></pre> <p>none of the following expressions will compile:</p> <pre><code>Integer[] result = method(new ArrayList&lt;Integer&gt;()); Number[] result = method(new ArrayList&lt;Integer&gt;()); Object[] result = method(new ArrayList&lt;Object&gt;()); </code></pre> <p>but</p> <pre><code>Number[] result = method(new ArrayList&lt;Number&gt;()); Object[] result = method(new ArrayList&lt;Number&gt;()); </code></pre> <p>will.</p> <p>Another example where subtyping matters is overriding. Consider:</p> <pre><code>Super sup = new Sub(); Number n = sup.method(1); </code></pre> <p>where</p> <pre><code>class Super { Number method(Number n) { ... } } class Sub extends Super { @Override Number method(Number n); } </code></pre> <p>Informally, the runtime will rewrite this to:</p> <pre><code>class Super { Number method(Number n) { if (this instanceof Sub) { return ((Sub) this).method(n); // * } else { ... } } } </code></pre> <p>For the marked line to compile, the method parameter of the overriding method must be a supertype of the method parameter of the overridden method, and the return type a subtype of the overridden method's one. Formally speaking, <code>f(A) = parametertype(method asdeclaredin(A))</code> must at least be contravariant, and if <code>f(A) = returntype(method asdeclaredin(A))</code> must at least be covariant.</p> <p>Note the "at least" above. Those are minimum requirements any reasonable statically type safe object oriented programming language will enforce, but a programming language may elect to be more strict. In the case of Java 1.4, parameter types and method return types must be identical (except for type erasure) when overriding methods, i.e. <code>parametertype(method asdeclaredin(A)) = parametertype(method asdeclaredin(B))</code> when overriding. Since Java 1.5, covariant return types are permitted when overriding, i.e. the following will compile in Java 1.5, but not in Java 1.4:</p> <pre><code>class Collection { Iterator iterator() { ... } } class List extends Collection { @Override ListIterator iterator() { ... } } </code></pre> <p>I hope I covered everything - or rather, scratched the surface. Still I hope it will help to understand the abstract, but important concept of type variance.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload