What is the difference between <? extends Base> and ?

前端 未结 4 1532
温柔的废话
温柔的废话 2021-02-01 03:24

In this example:

import java.util.*;

public class Example {
    static void doesntCompile(Map> map) {}
    static &l         


        
相关标签:
4条回答
  • 2021-02-01 04:06

    In the call:

    compiles(new HashMap<Integer, List<Integer>>());
    

    T is matched to Integer, so the type of the argument is a Map<Integer,List<Integer>>. It's not the case for the method doesntCompile: the type of the argument stays Map<Integer, List<? extends Number>> whatever the actual argument in the call; and that is not assignable from HashMap<Integer, List<Integer>>.

    UPDATE

    In the doesntCompile method, nothing prevents you to do something like this:

    static void doesntCompile(Map<Integer, List<? extends Number>> map) {
        map.put(1, new ArrayList<Double>());
    }
    

    So obviously, it cannot accept a HashMap<Integer, List<Integer>> as the argument.

    0 讨论(0)
  • 2021-02-01 04:20

    Simplied example of demonstration. Same example can be visualize like below.

    static void demo(List<Pair<? extends Number>> lst) {} // doesn't work
    static void demo(List<? extends Pair<? extends Number>> lst) {} // works
    demo(new ArrayList<Pair<Integer>()); // works
    demo(new ArrayList<SubPair<Integer>()); // works for subtype too
    
    public static class Pair<T> {}
    public static class SubPair<T> extends Pair<T> {}
    

    List<Pair<? extends Number>> is a multi-level wildcards type whereas List<? extends Number> is a standard wildcard type .

    Valid concrete instantiations of the wild card type List<? extends Number> include Number and any subtypes of Number whereas in case of List<Pair<? extends Number>> which is a type argument of type argument and itself has a concrete instantiation of the generic type.

    Generics are invariant so Pair<? extends Number> wild card type can only accept Pair<? extends Number>>. Inner type ? extends Number is already covariant. You have to make the enclosing type as covariant to allow covariance.

    0 讨论(0)
  • 2021-02-01 04:22

    I'd recommend you to look in documentation of generic wildcards especially guidelines for wildcard use

    Frankly speaking your method #doesntCompile

    static void doesntCompile(Map<Integer, List<? extends Number>> map) {}
    

    and call like

    doesntCompile(new HashMap<Integer, List<Integer>>());
    

    Is fundamentally incorrect

    Let's add legal implementation:

        static void doesntCompile(Map<Integer, List<? extends Number>> map) {
            List<Double> list = new ArrayList<>();
            list.add(0.);
            map.put(0, list);
        }
    

    It is really fine, because Double extends Number, so put List<Double> is absolutely fine as well as List<Integer>, right?

    However, do you still suppose it's legal to pass here new HashMap<Integer, List<Integer>>() from your example?

    Compiler does not think so, and is doing his (its?) best to avoid such situations.

    Try to do the same implementation with method #compile and compiler will obviously does not allow you to put a list of doubles into map.

        static <T extends Number> void compiles(Map<Integer, List<T>> map) {
            List<Double> list = new ArrayList<>();
            list.add(10.);
            map.put(10, list); // does not compile
        }
    

    Basically you can put nothing but List<T> that's why it's safe to call that method with new HashMap<Integer, List<Integer>>() or new HashMap<Integer, List<Double>>() or new HashMap<Integer, List<Long>>() or new HashMap<Integer, List<Number>>().

    So in a nutshell, you are trying to cheat with compiler and it fairly defends against such cheating.

    NB: answer posted by Maurice Perry is absolutely correct. I'm just not sure it's clear enough, so tried (really hope I managed to) to add more extensive post.

    0 讨论(0)
  • 2021-02-01 04:30

    By defining the method with the following signature:

    static <T extends Number> void compiles(Map<Integer, List<T>> map) {}
    

    and invoking it like:

    compiles(new HashMap<Integer, List<Integer>>());
    

    you're matching T against the type you're providing.

    In the jls §8.1.2 we find, that (interesting part bolded by me):

    A generic class declaration defines a set of parameterized types (§4.5), one for each possible invocation of the type parameter section by type arguments. All of these parameterized types share the same class at run time.

    In other words, the type T is matched against the input type and assigned Integer. The signature will effectively become static void compiles(Map<Integer, List<Integer>> map).

    When it comes to doesntCompile method, jls defines rules of subtyping (§4.5.1, bolded by me):

    A type argument T1 is said to contain another type argument T2, written T2 <= T1, if the set of types denoted by T2 is provably a subset of the set of types denoted by T1 under the reflexive and transitive closure of the following rules (where <: denotes subtyping (§4.10)):

    • ? extends T <= ? extends S if T <: S

    • ? extends T <= ?

    • ? super T <= ? super S if S <: T

    • ? super T <= ?

    • ? super T <= ? extends Object

    • T <= T

    • T <= ? extends T

    • T <= ? super T

    This means, that ? extends Number indeed contains Integer or even List<? extends Number> contains List<Integer>, but it's not the case for Map<Integer, List<? extends Number>> and Map<Integer, List<Integer>>. More on that topic can be found in this SO thread. You can still make the version with ? wildcard work by declaring, that you expect a subtype of List<? extends Number>:

    public class Example {
        // now it compiles
        static void doesntCompile(Map<Integer, ? extends List<? extends Number>> map) {}
        static <T extends Number> void compiles(Map<Integer, List<T>> map) {}
    
        public static void main(String[] args) {
            doesntCompile(new HashMap<Integer, List<Integer>>());
            compiles(new HashMap<Integer, List<Integer>>());
        }
    }
    
    0 讨论(0)
提交回复
热议问题