• Post Reply Bookmark Topic Watch Topic
  • New Topic

How to use generic arguments with or without wild cards? Super or Extends?  RSS feed

 
Ranch Hand
Posts: 417
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I'm confused about declaring function arguments as List<Integer> versus List<? extends Integer> verses  List<? super Number>. Aside from it being a bit cumbersome to initialize a List<Number>, they all seem to work fine.

(1) Is not List<Integer> completely redundant with List<? extends Integer> because whenever we specify a (non-primitive) type, we are liberty to use that type or a descendant of that type?
I think this is the Liskov substitution principle. So what is the advantage of List<? extends Integer>?

(2) And what about the difference between function parameters of List verses List<Object> verses List<?>? Are these all redundant too?

(3a) And lastly, is it possible to efficiently modify the elements of an existing list instead of creating and returning a new list? While I suppose you could call get and set for a function parameter of type List, I'm worried these would be very inefficient for large linked lists. C++, for example, has a special for-each-loop syntax so you receive a reference to the linked list element instead of a copy.

In other words:
(3b) What is Java doing when I type "for (Integer n: nums){ n = n + 1; }" and nums is a List<Integer>? Apparently it is making a deep copy because when I print out the original List in the main program (or in this case, test function), the original list is not modified. Is there a way to make this do a shallow copy so I modify the original and avoid creating a new list as the function value?

Here is my code that passes all 6 tests:
 
Sheriff
Posts: 11494
180
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Siegfried Heintze wrote:
(1) Is not List<Integer> completely redundant with List<? extends Integer> because whenever we specify a (non-primitive) type, we are liberty to use that type or a descendant of that type?
I think this is the Liskov substitution principle. So what is the advantage of List<? extends Integer>?

See https://docs.oracle.com/javase/tutorial/java/generics/inheritance.html
 
Junilu Lacar
Sheriff
Posts: 11494
180
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Siegfried Heintze wrote:
(2) And what about the difference between function parameters of List verses List<Object> verses List<?>? Are these all redundant too?

See https://docs.oracle.com/javase/tutorial/java/generics/subtyping.html
 
Junilu Lacar
Sheriff
Posts: 11494
180
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Siegfried Heintze wrote:
In other words:
(3b) What is Java doing when I type "for (Integer n: nums){ n = n + 1; }" and nums is a List<Integer>? Apparently it is making a deep copy because when I print out the original List in the main program (or in this case, test function), the original list is not modified. Is there a way to make this do a shallow copy so I modify the original and avoid creating a new list as the function value?

Java isn't doing a copy here, you are. All parameters in Java are passed by value. In the case of reference variables, the value is the location of the object being referenced. So, in your incrXXX() methods, the List parameter, nums, references the same object that was passed by the test method. Since the list elements are Integer objects, they are immutable so you cannot simply mutate them and replace each element with a new value as you iterate over the list in the for loop. You'd have to add/remove elements to the list passed as a parameter, nums, instead of calling add() on the local list, r, as you are doing now.

If you had a mutable object, then it would work as you want it to.
 
author
Sheriff
Posts: 23295
125
C++ Chrome Eclipse IDE Firefox Browser Java jQuery Linux VI Editor Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Siegfried Heintze wrote:
(1) Is not List<Integer> completely redundant with List<? extends Integer> because whenever we specify a (non-primitive) type, we are liberty to use that type or a descendant of that type?
I think this is the Liskov substitution principle. So what is the advantage of List<? extends Integer>?


No. That is not what the wildcards are used for.

A List<Integer> is a List where the generic type is Integer. A List<? extends Integer> is a List where the compiler does not know what the generic type is -- except it can be an Integer or some type that extends Integer.

With wildcards, it is possible to write methods that can support a multitude of generic types.  For example, if I want to just print out a List, I can have a method that takes a List<?>, instead of having to support List<String>, List<Integer>, List<Double>, etc. separately.

[EDIT: Start to answer. Walk away. Come back to complete.... and there are already lots of answers... ]

Henry
 
Junilu Lacar
Sheriff
Posts: 11494
180
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
As for what exactly happens with this code:

The for-loop iterates over the nums List. Line 2 is pretty useless. First, it unboxes the Integer, adds 1 to it and boxes up the result in a new Integer. This new Integer object is then made eligible for garbage collection in the next iteration because the loop variable n is now assigned a reference to the next element of the list nums. Again, this behavior is due to the fact that Integer objects are immutable.  It's the same thing that happens when you do this:

Since Strings are immutable, the object that variable s references on line 1 is different from the object that it references after line 2 is executed. That is, after line 2 executes, there are two String objects in memory. The first String is "Hello" and the second String is "Hello, World".  On line 1, variable s refers to the first String. After line 2, variable s refers to the second String.
 
Ranch Hand
Posts: 461
22
Android Chrome Eclipse IDE Google App Engine Java Notepad Oracle Ubuntu Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Seigfried wrote: (1) Is not List<Integer> completely redundant with List<? extends Integer> because whenever we specify a (non-primitive) type, we are liberty to use that type or a descendant of that type?
I think this is the Liskov substitution principle. So what is the advantage of List<? extends Integer>?
Bounded wildcard and the paramaterized Type(no bounds),they both have their own use,it completely depends upon the context for which is suitable.for ex-In List<Number> you can add a Number to it and you are sure that you will get a Number from it.Now lets take a another List with bounded wild card List<? extends Number> lets analyse the possibility for unknown(?),it is called capturing the types,the posibilities for this List can be:
List<AtomicInteger>
List<AtomicLong>
List<BigDecimal>
List<BigInteger>
List<Byte>
List<Double>
List<Float>
List<Integer>
List<Long>
List<Short>
so compiler can make these assumption for the ?(unknown,wildcard) in accordance with the bound.what can you expect for this List.I meant some property that is common amongst all these 10 List,so that compiler can be sure about it.this is *you can expect a Number retrieval  from any of these List* but you cannot add some specific type of instance to it as compiler cannot anticipate about that Specific List(of those,mentioned above) which it would be at run time.if you will try adding Number it can think the List may be List<AtomicInteger>(or some another).so for the compile time safety compiler will not allow it to add any instance.
For these reason if you think that you will use the list for only retrieval i.e.,List will act as a producer then use these type of bounded wildcard.now think about another kind of bound is List<? super Number>,think about its speciality.the concept  is  explained as "PECS"(producer extends consumer super,mnemonic) by Joshua Bloch in his famous book Effective java.
Liskov Substitution principle have to do with super class and base class.but as generics are erased after compilation.for the same reason we have the same class for List<Integer> or List<of anything else> and property remains the same for each type.so IMO,it has not to do anything here.
Seigfried wrote:(2) And what about the difference between function parameters of List verses List<Object> verses List<?>? Are these all redundant too?
List is a raw type,you can add an instance of any type to it but if you will retrieve from it then you have a type-Object.
List<Object> is type safe in a way suppose if i have a method which is declared to take a raw type List then we can pass an argument of type List<Integer> or any other type but if the method is declared to take List<Object> then you cannot pass List<Integer> or List of other types except List<Object>.List<?> is also typesafe but you cannot expect anything about the unknown,i think this is already cleared in the 1st question.
Seigfried wrote:(3a) And lastly, is it possible to efficiently modify the elements of an existing list instead of creating and returning a new list? While I suppose you could call get and set for a function parameter of type List, I'm worried these would be very inefficient for large linked lists. C++, for example, has a special for-each-loop syntax so you receive a reference to the linked list element instead of a copy.
Their are many List implementation in the Java Collection framework.efficient modification will depend on the context which means what kind of operations are performed on the list(so that you can choose the best implementation of list having good time complexity for the algorithm).their are plenty of threads like *LinkedList vs ArrayList* in this forum like:
this,
this,
or
search this topic on ranch.
As the List is mutable you can modify it and return it in the method.but i will not recommend you to return the same list as its mutability also gives the same right to its client and thus you cannot expect about the behavior of your program at users end.Since we are discussing about modification.please note it that whichever implementation of list you are using don't forget about the iterator behavior about-fail safe,fail fast...you will get these things in the API.
--------------
your last question has been well answered by junilu.
--------------
Seigfried wrote:Here is my code that passes all 6 tests
though,your code passes a six test but they will fail in certain circumstances.
Have a look:

Here the List named "nums" is typesafe and will not fail even but it  can only be given a List<Integer>.it can be more flexible,if you will notice the nums is a producer so it can be declared as List<? extends Integer>.

This is fine!
This is not a good implementation of generics as i can pass List<Object> as nums which can even contain String,for the same reason compiler will generate a warning for the unchecked cast and thus their is a very good chance that you will get a ClassCastException at runtime.Even you had use the generics in your code it doesn't make it a compile time safe.
for the incrObject and the incrWildCard,the same reason is applied here as for *incrSuperNumber*.it cannot be guaranteed that the list will contain the Integer(at Run time).
and the incr is a Raw type so it is not safe.
May be you are aware of these thing but i have just enumerated it for why the bounded wildcard,the incrExtendsInteger,is suitable here.suppose a case that the nums are also required to consume the instances along with the production then in that case List<Integer> is a good choice.

[Note]:Generics provides a *compile time safety*.sometimes(by several like us),this definition seems misleading.generics provides a safety to the program to safely execute during the run time,but the steps taken for the safety are provided at compile time..

Hope It Helps!

Kind Regards,
Praveen.
 
Marshal
Posts: 56600
172
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Siegfried Heintze wrote:. . . (1) Is not List<Integer> completely redundant with List<? extends Integer> because whenever we specify a (non-primitive) type, we are liberty to use that type or a descendant of that type?
I think this is the Liskov substitution principle. So what is the advantage of List<? extends Integer>?
Don't call subtypes children or descendants because biological inheritance and money inheritance are different from inheritance of classes.
I shall refer you to the Java™ Tutorials (link 1) (link 2). You have chosen a bad example because Integer is a final class and it hasn't got any subclasses to extend it. It would mean that a List<? extends Integer> is a subtype of List<Integer>, but the compiler doesn't know which subtype of List, so you can get objects out of the List but cannot add objects to it
(2) And what about the difference between function parameters of List verses List<Object> verses List<?>? Are these all redundant too?
No. For the same reason you can add things to a List<Object> and get them back but I don't think you can add things to a List<?>
(3a) And lastly, is it possible to efficiently modify the elements of an existing list instead of creating and returning a new list?
You can modify all mutable reference types if you can obtain a reference to the object. It is therefore possible to iterate a List and call methods to change the state of any or all its elements.
. . . C++, for example . . .
C++ is  different language, completely unrelated to Java®. The apparent similarities encouraged C++ programmers to try Java® in its early days, but I think nowadays they cause more confusion than help.
(3b) What is Java doing when I type "for (Integer n: nums){ n = n + 1; }" and nums is a List<Integer>? Apparently it is making a deep copy . . .
I think you have misunderstood the for‑each loop in Java® (officially called the enhanced for statement). You will see a version of a for‑each loop in the peach rectangles with orange border not surrounded by pale blue, with an explanation in the JLS's inimitable incomprehensible style. What it means is that a copy is taken of each element in your List. You can call methods on that copy to change the state of the element. Again you have chosen the wrong type because Integer is immutable. Had you chosen a mutable type, you would have been able to change its state with a method, e.g. myNumber.increment(1). But none of the subtypes of java.lang.Number has such an increment method, so what you are doing is not calling a method of the List's element at all. You are unboxing it and doing arithmetic and re‑assigning the result to the (boxed) copy in the loop. Since you are only affecting the loop copy, the List remains unchanged. Remember that the for‑each loop uses the List's Iterator behind the scenes, and Iterators do not allow structural change to their parent Collection by any other means, so you must regard a for‑each loop as presenting you with a read‑only view of the List. There is no copying of the List itself, whether deep or shallow.

I shall look at your code in a further post.
 
Campbell Ritchie
Marshal
Posts: 56600
172
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Siegfried Heintze wrote: . . . Here is my code that passes all 6 tests:
Lines 6‑12: You are taking elements out of a List<Integer> doing arithmetic with the copy reference and adding that copy to a new List<Integer>. Easy to explain.
Lines 15-21: you are taking elements out of a List of unknown but you know they will all return true if you try e instanceof Integer so when you do arithmetic you can unbox them and add the result which is again the loop copy to a new List.
Lines 24-30: You are again taking elements out of a List of unknown, which might be Object or Number. Now an Integer does extend Number, and you are asserting the elements are Integers by the cast to an int after unboxing. Any other type of Object or Number will cause an exception from the cast.
Lines 32-28: You can always take elements out of a List and you are again asserting they are Integers because you can unbox them and cast them to ints.
Lines 40-47: You can add any type to a List<Object> and get its elements out: you are making the same assertion that they are Integers with the cast. Had you added a Long to that List, you would have suffered an exception at line 43.
Lines 48-54: You are receiving the List as a raw type: it could contain any type of element, and the cast to int will cause an exception.
Lines 56-58: All Number objects have an intValue method, so you can convert the Stream<Integer> to an IntStream and call its toArray method.

You haven't shown the correctness of that code. You have used an immutable type; you shoul‍ld have used a mutable type with subtypes, so you can see the effect of putting subtypes and supertypes into the Lists.
You s‍hould also have added other types of Number to the Lists, in which case you would have seen the effects in the form of exceptions. The whole idea of generics is to remove the need for casts; your frequent casts show you are not testing the code intensively enough.
 
Siegfried Heintze
Ranch Hand
Posts: 417
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thank you everyone. That was very helpful.

Siegfried
 
Campbell Ritchie
Marshal
Posts: 56600
172
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
That's a pleasure
 
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!