javagenericsinheritancewildcardbounded-wildcard

Why can I use bounded wildcards for parameter and not for return type in a method?


I couldn't find a better description of my question's topic, so I will try to better explain my problem. I noticed that if I use bounded wildcards I can use its "bound" as an argument, but not as a return value. This might sound confusing, so I will paste my code straight away.

public static void main (String[] args) {
    List<?> wildcardList = new ArrayList<> ();
    List<String> stringList = new ArrayList<> ();
    takeList (wildcardList); //compiles
    takeList (stringList); //compiles
    /////////////////////////////////////
    List<?> wildcard = returnList (); //compiles
    List<String> strings = returnList (); //doesn't compile

}

static void takeList(List<? extends Object> list){
    //some code
}

static List<? extends Object> returnList(){
    return new ArrayList<> ();
}

I want to know why the last line of code doesn't compile. What is the problem in that case when I explicitly said that return type for returnList() is <? extends Object>. And clearly String IS-A Object. Could someone help me clear this up?


Solution

  • Let us focus on two lines:

    // f() returns a List<? extends Object>
    List<?>      a = f(); // ok: can never do unexpected things
    List<String> b = f(); // error: not 100% safe
    

    For case a, there is no error, as a List<? extends Object> is exactly a List<?>.

    For case b, the question for the Java compiler* is whether any sort of unexpected error (class cast) could be generated if it allowed the assignment to go through. Let us look at some examples:

    The compiler is rightfully worried that you may use it improperly. Yes, it could have allowed you to go forward by checking that, indeed, you were not calling b.add() -- but that would complicate the compiler for very little benefit. There are many other places where compilation errors could be avoided by verifying that nothing bad will actually happen, but having simple rules ("nothing risky unless explicitly casted away") makes the rules easier to follow, and defaulting to "only allow safe things" is very much part of the Java language.

    To compare:

    // f() returns a float
    float a = f(); // ok
    int   b = f(); // error: not 100% safe, unwanted rounding may result!
    

    (*) - I use "compiler" in a broad sense, to refer both to the language and the compiler.