2

While I was going through some generics question I came across this example. Will you please explain why list.add("foo") and list = new ArrayList<Object>() contain compailation issues?

In my understanding List of ? extends String means "List of Something which extends String", but String is final ? can only be String. In list.add() we are adding "foo" which is a string. Then why this compilation issue?

public class Generics {
public static void main(String[] args) {
}

public static void takelist(List<? extends String> list){
    list.add("foo"); //-- > error
    /*
     * The method add(capture#1-of ? extends String) in the 
     * type List<capture#1-of ? extends String> is not applicable 
     * for the arguments (String)
     */

    list = new ArrayList<Object>();
    /*
     * Type mismatch: cannot convert from ArrayList<Object> to List<? extends               String>
     */
    list = new ArrayList<String>();
    Object o = list;
}

}

1
  • Object does not extend String, but it is a super type. Commented Jul 13, 2016 at 8:01

4 Answers 4

2

For starters, the java.lang.String class is final, meaning nothing can extend it. So there is no class which could satisfy the generic requirement ? extends String.

I believe this problem will cause all of the compiler errors/warnings which you are seeing.

list.add("foo");                  // String "foo" does not extend String
list = new ArrayList<Object>();   // list cannot hold Object which does not extend String
Sign up to request clarification or add additional context in comments.

5 Comments

It doesn't lead to the second compilation error: you wouldn't be able to make that assignment, even if the parameter were List<String> list.
The OP has already stated that knows String is final. And the statement List<? extends String> s; is a perfectly valid declaration. If I say List<T extends String> that means that T either extends String or it is String itself.
@Teto If T extends String be valid, then why would there be an error when adding "foo" to the list?
@Tim, At compile time, the compiler doesn't know what T is. If you have a List of T, where T extends String, you can't put a String in that list because at run time T might not be String. I wasn't suggesting that the user switch from "?" to "T".
list = new ArrayList<String>(); is valid, there is no compilation error on that line.
1

It is true what you say. String is final. And so you can reason that List<? extends String> can only be list of string.

But the compiler isn't going to make that kind of analysis. That is to say, the compiler will not assess the final or non-final nature of String (see comments). The compiler will only let you put null into your list.

You can pull stuff out though.

String s = list.get(0);

4 Comments

To make your answer a bit more explicit, I might add after "that kind of analysis": "the compiler makes no distinction between non-final and final classes with respect to generics".
I think you should add that no check is being made to see whether or not String is a final class at compile time. So the compiler happily builds your code, but at runtime, since nothing extends String (not even String itself), you can't add anything to the list.
@AndyTurner, Thanks for the help. I made some clarification.
@TimBiegeleisen I am not sure I understand your remarks. At run time, the generics become the raw type. Effectively it is List<Object>. It will happily let me put whatever I want in there at run time. It is at compile time that I am prevented from putting anything into "list of something". If I were allowed to put anything in, even a string, then that would be a problem at run time if it turned out that "something" was not a string.
1

While String is final, this information is not used.

And in fact, with Java 9, it may no longer be final (rumor has it that Java 9 may finally get different more efficient String types).

Without knowing it is final, List<? extends String> could be e.g. a List<EmptyString> of strings that must be empty.

void appendTo(List<? extends String> l) {
  l.append("nonempty");
}

appendTo(new ArrayList<EmptyStrings>());

would yield a violation of the generic type.

As a rule of thumb always use:

  • ? extends Type for input collections (get is safe)
  • ? super Type for output collections (put is safe)
  • Type (or maybe a <T>) for input and output collections (get and put are safe, but the least permissive).

I.e. this is fine:

void appendTo(List<? super String> l) {
  l.append("nonempty");
}

appendTo(new ArrayList<Object>());

Comments

0

You have already mentioned that String is a final type and therefore there is no point in repeating this fact. The point that is important to note is that none of the following Lists allows adding an element:

  1. List<?> which is a List of anything.
  2. List<? extends SomeType> which is a List of anything that extends SomeType.

Let's understand it with an example.

The List<? extends Number> could be List<Number> or List<Integer> or List<Double> etc. or even a List of some other type that hasn't been defined yet. Since you can not add any type of Number to a List<Integer> or any type of Number to a List<Double> etc., Java does not allow it.

Just for the sake of completeness, let's talk about List<? super Integer> which is List of anything that is a super/parent type of Integer. Will the following compile?

Object obj = 10.5;
list.add(obj);

As you can guess, of course NOT.

What about the following?

Object obj = 10.5;
list.add((Integer) obj);

Again, as you can guess, indeed it will compile but it will throw ClassCastException at runtime. The question is: why did not Java stop us in the first place by failing the compilation itself? The answer is Trust. When you cast something, the compiler trusts that you already understand the cast.

So, the following compiles and runs successfully:

Object obj = 10;
list.add((Integer) obj);
list.add(20);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.