0

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 .

  1. Understanding upper and lower bounds on ? in Java Generics
  2. Java lower bound wildcards

If anyone can explain I will be obliged.

BigIO
  • 111
  • 8
  • 1
    You can call your method `myadd` only with a `List super Number>` or a `List` or a `List` (because `Object` is the only supertype of `Number`). That's it. Oh, I forgot the _raw type_ `List`, but you shouldn't use it at all. – Seelenvirtuose Jul 27 '22 at 17:59
  • 3
    Are you asking about `myadd(empty);` or the fact that your `add(..)` calls all compile? – Sotirios Delimanolis Jul 27 '22 at 18:04
  • 1
    It's hard to understand where you have your problems. To which line exactly does your question "why this method executes perfectly fine" refer to? – Seelenvirtuose Jul 27 '22 at 18:09
  • It says nums is the list of any subclass of Number. – Cheng Thao Jul 27 '22 at 19:42
  • @ChengThao According to oracle docs super > means any same or super class of that type not subclass ? – BigIO Jul 28 '22 at 12:00
  • @Seelenvirtuose my problem is int float double are not super classes of Number , the only super class is Object . Then why I am able to add int float double to my list as according to the definition of lower bound i should only be able to add Object class ( only super class of Number ) not subclass of Object class (int float double..) ? – BigIO Jul 28 '22 at 12:06
  • First of all you are dealing with `Integer`, `Float` and `Double` - not with `int`, `float` and `double`. Second, an `Integer` is an `Object`, so you can use an `Integer` everywhere, where an `Object` is expected. That is a fundemental rule in polymorphism. – Seelenvirtuose Jul 28 '22 at 13:59
  • Yeah I agree with you it is respective wrapper classes of int, float, double . But Integer is a subclass of Object class(basically subclass of Number) & if sub classes are also allowed in " ?super " then what is the difference between extends & super? – BigIO Jul 28 '22 at 14:40
  • @BigIO, you are correct. That was what I was thinking but the writing came out wrong. – Cheng Thao Jul 28 '22 at 15:01

1 Answers1

6

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-a Number instance, it can be contained in a List<Number>—or, for that matter, a List<Object>" I don't agree since Long, Double all these wrapper classes are not instances of Number class. Instead they are instances of sub-classes of Number 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 be List<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>?

erickson
  • 265,237
  • 58
  • 395
  • 493
  • Then what is the difference between both of them because ultimately i am able to add subclasses of Number in both extends & super ? – BigIO Jul 28 '22 at 12:08
  • Please Justify what you said in 2nd paragraph of your answer you said " Because any Long, Double, etc. is-a Number instance, it can be contained in a List—or, for that matter, a List" I don't agree since Long, Double all these wrapper classes are not instances of Number class. Instead they are instances of sub-classes of Number class. – BigIO Jul 28 '22 at 15:56
  • 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 be List." 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??? – BigIO Jul 28 '22 at 15:56
  • 1
    @BigIO I tried to address your questions in an update to my answer. – erickson Jul 28 '22 at 17:34
  • 1
    I was not able to comprehend that sublcasses (Is-A relationship) of Number can also be added to ?super Number since it IS-A Number after all . – BigIO Jul 29 '22 at 16:24
  • 2
    Excellent! I think it's great that you have a desire to really understand how things are working. Too many developers lack that interest. – erickson Jul 29 '22 at 16:33
  • With your last code snippet and elaborative explanation now I understood this- In List extends Number > we can add any Number or subclasses(IS-A) of Number & while assigning we can assign it to Number since we know it is at least a Number ( that's why it is called Upper Bound). – BigIO Jul 29 '22 at 16:35
  • Now in case of List super Number> any element which is Number or its super class ( Object can be added to it . Since sub class of Number class IS-A Number too so we can add that too . But when it comes to assigning we can only assign Object safely since we it can be an Object too ( that's why lower bound ) – BigIO Jul 29 '22 at 16:36
  • So finally I can say extends Number > -- Number or sub class of Number & while assigning only Number can be taken safely ( Upper Bound ) super Number) -- Number (including sub classes of Number which forms IS-A relationship with Number class) or Super class of Number i.e. Object & while assigning only Object can be taken safely (Lower Bound) – BigIO Jul 29 '22 at 16:41
  • 1
    @BigIO I think you have it just right on assigning elements *from* the list *to* a variable, with the upper and lower bounds. (This is often called "consuming" a generic type.) But on adding elements to the list, it sounds like there is still some confusion. You can't (safely) add *anything* to a `List extends X>`. To a `List super X>` only `X` (subclasses but not its superclasses) can be safely added. (Passing a generically-typed argument like `list.add(x)` is sometimes called "producing".) – erickson Jul 29 '22 at 16:42
  • thank you but I am a rookie in the world of developers and this is my first question . I really did not expected that i will get to learn about so many things in just question . Thank you for your elaborative yet simple explanation . – BigIO Jul 29 '22 at 16:49
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/246902/discussion-between-bigio-and-erickson). – BigIO Jul 30 '22 at 07:26