3

So, I was looking through the Oracle Java Tutorials, specifically this piece of code;

List<EvenNumber> le = new ArrayList<>();
List<? extends NaturalNumber> ln = le;
ln.add(new NaturalNumber(35));  // compile-time error

which can be found here. It is my understanding (and please correct me if I'm wrong) that the above code will not work because the compiler does not know that lnrefers to a List of EvenNumbers and so this prevents you from accidentally adding any object that could be a supertype of the elements that are meant for the List. IF that is the case, then why is it that if you have a statement like Number num = new Integer(6); the compiler is able to correctly determine that num is an Integer object if you write an if statement like so; if (num instanceof Integer) {...}?

I guess my question is, how is the compiler able to determine that num is referring to an Integer object in the second example, but not able to determine that ln is referring to a List<EvenNumber> object in the first example?

2
  • 2
    "why is it that if you have a statement like Number num = new Integer(6); the compiler is able to correctly determine that num is an Integer object" - the compiler has no idea! instanceof is computed at runtime. Commented Sep 27, 2015 at 23:34
  • see also my article on wildcard in case it helps. Commented Sep 28, 2015 at 1:06

3 Answers 3

1

In general, the compiler is able to determine many things and it uses its observations when optimizing the code.

However, Java is statically and strongly typed language and compiler has no freedom when it comes to type safety.

1) Adding to the ln list is prohibited, because one does not know exactly which type of elements it is supposed to contain. In your example this rule prevented bringing the list to an invalid state.

2) "...the compiler is able to correctly determine that num is an Integer..."

Not true, the compiler must not determine it (although it could if Java were weakly typed language).

3) num instanceof Integer

This is evaluated at runtime, not at compile time. However, the following would produce the compile time error:

num instanceof String

because a Number can never be a String.

Sign up to request clarification or add additional context in comments.

2 Comments

Oops I'm sorry. I did mean "ln" and not "le". I edited my post. I did not realize that instanceof was evaluated at runtime. How is it able to figure out that num is an Integer object and not a Number object?
@BlaqICE Each Java object contains the information about its type: Object header.
1

Your two examples are the opposite of each other.

An Integer is a Number, so

List<Number> list;
list.add(new Integer(1)); // compiles

But EvenNumber is a NaturalNumber, not the other way around, so

List<EvenNumber> list;
list.add(new NaturalNumber(1)); // compile error

because while EvenNumber is a NaturalNumber, NaturalNumber is not (necessarily) an EvenNumber.

If you swap Integer and Number in your reference case then rework it, it should make sense, ie this will compile:

List<NaturalNumber> list;
list.add(new EvenNumber(2)); // compiles OK

2 Comments

I think you're describing a scenario in which List<NaturalNumber> ln = le; would occur, but that's not the case. That would cause a compilation error on the line above, but not when doing the add.
@Makoto No. I'm saying that his analogy of adding an Integer to a list of Number is irrelevant, because he has the opposite class hierarchy in his code - ie just as Integer is a subclass of Number, EvenNumber is a subclass of NaturalNumber, but his code is trying to add a NaturalNumber to a list of EvenNumber, which would be like trying to add a Number to a list of Integer... which obviously won't work.
0

The primary reason that this is not able to compile is due to the way that ? extends T behaves. In general, one should familiarize themselves with Producer Extends, Consumer Super, or PECS for short.

Now, I haven't looked at this particular code example, but I imagine the hierarchy being EvenNumber extends NaturalNumber. This makes the assignment statement valid, since an EvenNumber is a NaturalNumber (although I bet that the mathematicians are snickering about this).

In the scenario highlighted above, the reason you can't add anything to the collection is due it being primarily for reading from. That is, the list is a producer, so it is bound with extends.

If you want to both read and write to a collection, you would just leave off the wildcard.

You second example has nothing to do with generics at all, but rather simple inheritance. Since Integer inherits from Number, we can say that Integer is-a Number, and thus can be treated as a number. Any class that is inherited has this ability. Check the Liskov Substitution Principle for more info on that.

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.