Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference for <? super/extends String> in method and variable declaration

Given:

import java.util.*;

public class Hancock {
    //insert code here
        list.add("foo");
    }
}

Which two code fragments, inserted independently at line 5, will compile without warnings? (Choose two)

A. public void addString(List list) {
B. public void addString(List<String> list) {
C. public void addString(List<? super String> list) {
D. public void addString(List<? extends String> list) {

Correct answers are B & C.

Answers A and B are quite clear for me. For the answers C & D i know which way the inheritence is going, however i cannot understand why answer D does not compile in Eclipse while all others do (A with warrning about generic, B & C without warrings).

Error in Eclipse for answer D is The method add(capture#1-of ? extends String) in the type List<capture#1-of ? extends String> is not applicable for the arguments (String).

On the other hand this compiles:

public void addString() {
    List<? extends String> list1 = new ArrayList<String>();
    List<? super String> list2 = new ArrayList<String>();
}

Why? Why <? super String> does not compile in method declaration while it does compile in variable declaration.

I know that String is final class and cannot be extended by any other class but that does not explain to me what is going on here.

like image 726
Dáve Avatar asked Jul 21 '14 09:07

Dáve


1 Answers

First, let's see answer C:

public void addString(List<? super String> list) {
    list.add("foo");
}

This method declaration says that you will be allowed to pass List objects which are parametrized by some super class of String, for example String or Object. So:

  1. If you pass List<String> the list.add("foo") will be perfectly valid.
  2. If you pass List<Object> the list.add("foo") will be perfectly valid, because "foo" is a String (and you can add a String to a List<Object>).

This means that answer C is correct.


Lets now see answer D.

If you have a method declaration like this:

public void addString(List<? extends String> list) {

}

this means that you will be able to pass List objects parametrized by some unknown subtype of String. So, when you do list.add("foo"); the compiler won't be aware if the provided object has a type that matches the unknown subtype of String and therefore raises a compile-time error.


When you have:

public void addString() {
    List<? extends String> list1 = new ArrayList<String>();
    List<? super String> list2 = new ArrayList<String>();
}

This fragment compiles fine, because list1 is defined to hold List objects that are of some unknown subtype of String, including the String itself, which is why it's valid. The problem is that you won't be able to add anything, except null.

As for list2, the variable can hold List objects which are parametrized by some super-type of String, including the String itself.


More info:

  • What is PESC?
  • What is the difference between super and extends in Java wildcards?
like image 50
Konstantin Yovkov Avatar answered Oct 20 '22 03:10

Konstantin Yovkov