I know my question may seem like a duplicated one but i have read many of the similar questions and answers yet I am confused specifically with lower bounds (? super) . Please consider this code .
import java.util.*;
public class Main {
// just adds numbers to the List nums
static void myadd(List<? super Number> nums){
nums.add(1);
nums.add(1.0f);
nums.add(1.1);
nums.add(3l);
}
public static void main(String[] args) {
List empty= new ArrayList<>();
myadd(empty);
System.out.println(empty);
}
}
Here is the Link to execute it .
According to oracle docs
a lower bounded wildcard restricts the unknown type to be a specific type or a super type of that type
Number class is the superclass of classes Double, Float, Integer, Long, and Short then why this method executes perfectly fine ? This method (myadd) should only work for objects which are direct instances of Number class or it's super type , not for it's sub classes.
I have read following answers still my doubt is not clear .
If anyone can explain I will be obliged.
List<? super Number>
means that only List<Number>
(or some superclass of Number
, which is Object
) can be assigned to that variable (nums
, in your example).
Because any Long
, Double
, etc. is-a Number
instance, it can be contained in a List<Number>
—or, for that matter, a List<Object>
. However, if you were to assign an element from the list to another variable, the variable would have to be of type Object
, since the actual type of the list could be List<Object>
.
List<? extends Number>
would mean that List<Number>
or List<Long>
or List<Double>
or any other subclass of Number
may be assigned to that variable.
Because such a list is permitted to contain only a certain subclass of Number
—like Long
or Double
—but the particular subclass is unknown, nothing can be safely added to the list. Any of its elements can be assigned to a variable of type Number
, however.
Consider the following examples:
List<Object> objs = new ArrayList<>();
List<? super Number> nums = objs;
// objs is `List<Object>` so clearly this is fine:
objs.add(Integer.valueOf(1));
objs.add("Hello, World!");
// We've forgotten that nums is actually the same `List<Object>`, so this won't compile:
nums.add("Nice to meet you!");
// But we remember that nums is at least a `Number` so this is okay:
nums.add(Double.valueOf(1.0));
// This won't compile, because `nums` could/does have a `String` in it:
Number num = nums.get(0);
// This is okay; nums could hold any superclass of `Number`
Object obj = nums.get(0);
Then what is the difference between both of them because ultimately i am able to add subclasses of Number in both extends & super ?
No, this is wrong. As I said, "Because such a list is permitted to contain only a certain subclass of Number
—like Long
or Double
—but the particular subclass is unknown, nothing can be safely added to the list." You can't safely add a subclass of Number
(or anything) to a List<? extends Number>
. The ?
doesn't mean "any subclass"; it means "unknown subclass". It's a list of some subclass of Number
that you don't know.
List<Integer> ints = new ArrayList<>();
List<? extends Number> alias = ints;
// This won't compile, because you'd be polluting `ints` with a float.
alias.add(Float.valueOf(1F));
List<Number> anyNum = new ArrayList<>();
// These adds are fine, anyNum can contain any subclass of Number
anyNum.add(Integer.valueOf(1));
anyNum.add(Float.valueOf(1F));
// This is fine...
alias = anyNum;
// ...but these will fail, because we "forgot" that anyNum points to List<Number>
alias.add(Integer.valueOf(2));
alias.add(Float.value(2F));
Please Justify what you said in 2nd paragraph of your answer you said " Because any
Long
,Double
, etc. is-aNumber
instance, it can be contained in aList<Number>
—or, for that matter, aList<Object>
" I don't agree sinceLong
,Double
all these wrapper classes are not instances ofNumber
class. Instead they are instances of sub-classes ofNumber
class.
I used some jargon there, "is-a", with which you may be unfamiliar. When one class is a subclass of another, it should be fully substitutable for the superclass. A Long
or a Double
can perform any operation a Number
can; we say that a Double
"is-a" Number
. An assignment like Number x = Integer.valueOf(1)
is perfectly fine because Integer
is-a Number
. And of course, nums.add(x)
would be perfectly fine, because the type of x
is Number
. Would a (widening) cast from type Child
to type Parent
succeed? Then Child
is-a Parent
.
However I agree with this line "if you were to assign an element from the list to another variable, the variable would have to be of type
Object
, since the actual type of the list could beList<Object>
." because when i tried to assign a value from the list to a Float variable I had to typecast it to float .This raises more qestion because if List can only accept Object then why it is accepting Integer , FLoat .. in myadd() method???
A variable of type List<?>
means that the list has some type, but we don't know what it is. Nothing can be safely added to it, and if we take an element from it, all we know about the type is that it is an Object
or any subclass, thus we can only safely assign it to Object
. Don't think of List<?>
as "a list of any type", but as "a list of unknown type."
A variable of type List<Object>
contains anything that "is-a" Object
. Since any type ultimately extends Object
, any type can be added to this list.
A List<Number>
contains anything that "is-a" Number
. Long
, Double
, etc. have that "is-a" (subtype) relationship with Number
, so they can be added to the list. I can define my own custom Number
type and add it to the list.
A variable of type List<? extends Number>
means that the list has some type, but we only know that the type is Number
or a subclass. We still can't add anything to it safely, because we don't know if it's an alias for a List<Long>
, List<Double>
, or what have you. But, if we take an element from it, we can assign it to Number
instead of Object
, because we know at least that it is-a Number
.
A variable of type List<? super Number>
means that the list has some type, but we only know that the type is Number
or a superclass. We can add anything that "is-a" Number
(which includes subclasses). But, if we take an element from it, we don't know it is a Number
; the alias could point to a List<Object>
, so it might contain objects of any class. We can only safely assign its elements to variables of type Object
.
You can't use wildcards when you instantiate a generic type. That is, you can't write new ArrayList<? extends Number>
. Wildcards are only used like an alias. A value with no wildcard is only aliased under a type with a wildcard, whether during assignment to a variable, passing as a parameter, or returning as a method result.
I keep using the word "safely", because Java generics are about type-safety. If you don't suppress or ignore any type-safety warnings, you won't have any class-cast exceptions at runtime unless there is a corresponding explicit cast in your code. If you suppress or ignore type-safety warnings, you might encounter a class cast exception from a point in your source code where no cast is visible. The compiler inserts these implicit casts for you in order to support generic types.
Back to your original example, let me rewrite it and ask you where your question lies.
static void myadd(List<? super Number> nums) {
/* Create Number objects */
Number anInteger = 1;
Number aFloat = 1F;
Number aDouble = 1D;
Number aLong = 1L;
/* Add Number objects to list */
nums.add(anInteger);
nums.add(aFloat);
nums.add(aDouble);
nums.add(aLong);
}
Do you not understand that a Integer
, Float
, Double
, and Long
can be assigned to a Number
? Or do you not understand that a Number
can be added to a List<? super Number>
?