Reputation: 11
public static void print(List<? extends Number> list) {
for (Number n : list)
System.out.print(n + " ");
System.out.println();
}
public static void main(String[] args) {
ArrayList<Integer> al = new ArrayList<>();
print(al);
}
I was wondering what's the type of list
(the parameter). is in the print()? is it Number
? I think it is Number
because if it is not the Number
, how do we explain the Number n
in the for loop? Do we usually use this in actual situations?
Upvotes: 1
Views: 113
Reputation: 103823
Your question uses ambiguous wording but asks about how we speak of things, so that's a problem. The type of the list
param is List<? extends Number>
- it's right there in the text. It's obviously not Number
, it is a list. However, your question sounds like what you want to ask is: What is the component type of it.
It is ? extends Number
. It's right there in the text. No, that's not the same as Number
. You are trying to oversimplify generics, and as the word suggests, you can't do that. The concept (not in java, in physics / the world / mathematics) is not that simple.
What you're looking at, the word you want to look for, is variance.
In java, generics have 4 different variances. These are all lists whose component type allows Number
, but they are all just different:
Variance name | What it looks like |
---|---|
Invariant | List<Number> |
Covariant | List<? extends Number> |
Contravariant | List<? super Number> |
Legacy/Raw | List |
The reason we need 4 is because there are 4 different things you may want to do with a 'list of numbers'.
Specifically, you can write to a list of numbers and read from it:
list.add(5.0); // 5.0 is a Double which is a kind of number
Number n = list.get(0);
There's writing and reading examples.
If you write a method that does both, then only a List<Number>
is allowed. Clearly. Imagine that you wrote:
void addFiveAndAHalf(List<Number> n) {
n.add(5.5);
}
and it was legal in java to pass List<Integer>
to this method (After all, Integer is a kind of number... passing 5
(an Integer
) to a method with sig foo(Number n)
is legal, why wouldn't passing a List<Integer>
be legal)? - then you end up with a double in your list of ints. whoops.
So, where basic types are covariant (see below), generics just aren't. They are invariant, only exactly the right type will do, a subtype is not good enough.
But, then, you do get to read and write to it.
However, if you write a method that only reads (technically, only does things for which the precise type doesn't need to be known, i.e. does not call any method whose parameters include the generics, such as add
or addAll
), then this rule goes away. Now writing a method that expects a list of numbers is just fine with being handed a list of integers, if all it does is read. All integers are numbers. But java does not know this, and is not going to analyse your code to try to figure out. Even if it could do that, that means you can never change that method ever again in the future because it might change how that method's very signature is treated. That's no good: The programmer needs to explicitly spell it out.
And you can do that:
void foo(List<? extends Number> n) {
Number a = n.get(0); // legal
n.add(5.5); // compiler error!
}
This method effectively says: It won't call any method on the list that needs the type (so, no add
or addAll
), therefore if you want to hand me a list of some subtype of Number instead, that's just fine.
Contravariance is much more rare and shows up if you want to invoke methods that need the type as parameter, but you never have any need for the generics type in any return type. For example, List's public E get(int idx)
method, if you never call that, but you DO call add
in your method, you want Contravariance: Any supertype is fine, but subtypes are not:
void foo(List<? super Number> n) {
n.add(5.5); // compiles
Number a = n.get(0); // compiler error
}
After all, adding a Double to a List<Double>
is fine. It's also fine to add it to a List<Number>
or List<Object>
.
If you want to say it in words, the type of this parameter is "A list of covariant Number". But java coders would never say this when talking to each other, they'd just say "A list of extends Number" at best, or just 'A list of numbers' and the covariance is lost in the simplified non-technical chatter.
void foo(List<Number> n) {
//
Upvotes: 2