Win a copy of Re-engineering Legacy Software this week in the Refactoring forum
or Docker in Action in the Agile forum!
  • Post Reply
  • Bookmark Topic Watch Topic
  • New Topic

Master Exam / Generics

 
John Stark
Ranch Hand
Posts: 185
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi,

There is a question asking what fragments can be inserted into the following code:

One correct answer is:


I tried it out and it compiles. But when I call the method with some List as parameter what is the actual type of T? For example when I do


Then T is of type Number? What is the effect of the question mark in (List<? extends T> input)?

Thanks,

John
 
Joseph Arnold
Ranch Hand
Posts: 42
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
In this case, T is of type number. The function can take any parameter that is of type T or is a sub class of T.
 
Helen Ma
Ranch Hand
Posts: 451
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
1. public static<T> List<T> backwards(List<? extends T> input){...}
says backwards method takes a list of type T and returns a list of type T. The compiler does not know what T is , so it can only take T as argument.

2. How about this?
public static<T> List < ? extends T> backwards( List<? extends T> input) { .. .}
says backwards takes a list of type T and returns a list of type T or some subtypes of it.
Let's say if you have Animal class and it has a Bird subtype and you do this:

List<Animal> input = new ArrayList<Animal>();

2.1 List <? extends Animal> output = backwards(input); //compiles , the compiler says "Hey programmer, backwards returns a ArrayList<Animal> , List<? extends Animal> = new ArrayList<Animal>();

2.2 List<? extends Object> output = backwards(input) ; //compiles, the compiler says "Hey, backwards returns a ArrayList<Animal> , List<? extends Object> = new ArrayList<Animal>();

2.3 List<Object> output = backwards (input) ; //won't compile the compiler says "Hey, List<Object> a = new ArrayList<Animal> won't work.

2.4 List <? super Animal> output = backwards (input) //won't compile , the compiler says "Hey, what happens if backward returns new ArrayList<Bird>?

2.5 List<Animal> output = backwards(input) //won't compile, the compiler says "Hey, what happens if List<Animal> a = new ArrayList<Bird> ?

Correct me if I am wrong.
 
Helen Ma
Ranch Hand
Posts: 451
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
One more point I want to make, correct me if I am wrong.

static <T> List<T> backwards (List<? extends T> input){...}

When the compiler sees this method declaration, the compile will say " I can only take Type T object, I don't know what T is until the program is run. I can only take List<T>. If you give me List<Animal>, I will take it as the argument. If you give me List<Bird>, I will take it as the argument. "

Therefore, backwards will return List<Animal> if you pass in List<Animal> and etc.....

List<Bird> input = new ArrayList<Bird>();
When you use backward methods like this in your main method, List<? super Animal> a = backwards(input) ; the compile will say "Hey, you give me List<Bird>, I output a List<Bird>. But you assign me List <? super Animal> , I won't compile it!"

 
Helen Ma
Ranch Hand
Posts: 451
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
One more thing:
if you have this:
public static <T> List< ? extends T > backward(List<? extends T> input){ ...}

The compiler says " Hey, you pass in a List<T> and I return you a List of T or T subtypes.

In the main method , you wrote this:
List<Animal> input1= new ArrayList<Animal>();
input1.add(new Bird());
List <Bird> output = (List<Bird>) backward(input1);
1. You notice a warning in ( List<Bird> )backward(input1);
2. The compiler warns you "Hey, you pass in a list of animal, I don't know you add a bird object to it until you run the program. I can return a list of animal or its subtype, bird to you as I promise.
It is up to you to take the risk to type cast it a list of bird.

In the main method , you wrote this:
List<Animal> input1= new ArrayList<Animal>();
input1.add(new Bird());
List <Basket> output = (List<Basket>) backward(input1);
1. You notice a compilation error on the last line.
2. The compiler yells at you " Hey, I can only return a list of animal or a list of animal subtypes, bird. You type cast it into a list of Basket, which is not a subtype of Animal!"

 
John Stark
Ranch Hand
Posts: 185
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi Helen,

Thanks for the nice examples, they are pretty clear.
What is the meaning of the '?' for the method parameter type?

What is the fifference between

and

?
John
 
Anayonkar Shivalkar
Bartender
Posts: 1557
5
Eclipse IDE Java Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi John,

Below is the difference:


This means that 'input' must be list of type T.


This means that 'input' can be list of anything 'assignable' to type T (i.e. anything which is-a T).

To make is more clear:
Now, in first definition, since output is a list of type T and input is also list of type T,

is valid code.

But, in second definition, input is not a list of type T, but it is list of sub-type of T. Hence

is not a valid code (it gives compile time error).

There are, however, two important things to remember:
1) List<? extends T> does not have is-a relationship with List<T>. Yes, all subtypes of T can be added to List<T>, but not to List<? extends T>. e.g. List<Animals> can contain Dog, Cat etc. but List<Dog> can contain only Dog, not Cat.
2) For above mentioned reason, when we pass List<? extends T> as argument, we cannot add anything to that list (that would be compile time error). e.g. if we pass List<? extends Animal>, compiler will not know if it is List<Cat>, List<Dog> or something else. Further, it won't even know if programmer is adding new Cat() to a List<Dog>. So, it doesn't allow to add anything altogether.

I hope this helps.
 
John Stark
Ranch Hand
Posts: 185
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Now, in first definition, since output is a list of type T and input is also list of type T,

is valid code.

But, in second definition, input is not a list of type T, but it is list of sub-type of T. Hence

is not a valid code (it gives compile time error).

Doh, that is indeed true. Thanks for that Anayonkar.

John
 
Helen Ma
Ranch Hand
Posts: 451
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Some more examples about <? extends E> as parameters :
Case 1 Generic parameter with class definition :
ArrayList class has a generic parameter <E> . It has addAll(Collection<? extends E> c) {....} method.
The compiler says about this class : Hey programmers, instantiate an ArrayList object with any type E, you can pass in a Collection with any type E or below it to addAll method and I will add the collection to the array list.

When programmer writes this Collection< ? extends Bird> c = new ArrayList<Bird>() ; ArrayList<Animal> alist = new ArrayList<Animal> ; a.addAll(c);

The programmer says: Hey compiler, I have a collection of object of Bird type or its subtype, please add the collection to alist.
Compiler says: c is a collection of Bird type , which is a subtype of Animal, so I am happy.

Case 2 Generic parameter with generic method:
Some classes may not have generic parameter, but it has generic methods.
static <T> List<T> backwards (List< ? extends T> a , List<T>b) { ..... }
The compiler says : Hey programmer, I accepts any type T, pass me a list of T and a list of T or its subtype, I will return you a list of T.
case 2.1
Programmer : List < Animal> b1 = new ArrayList<Animal>(); List<?extends Animal> a1 = new ArrayList<Bird>();

List<Animal> result = backwards(a1, b1);
Compiler verifies : I takes T, b1 is an animal list, so I substitute T with Animal . a is T or subtype, a1 is ok. I return List<Animal> as T is replaced by Animal, so List<Animal> result = .... compiles.....

What about this ?
case 2.2
Programmer says :
List < ? super Animal> b1 = new ArrayList<Animal>();
List<? extends Animal> a1 = new ArrayList<Bird>();

List<Animal> result = backwards(a1, b1); //wont' compile.
Compiler yells : Hey, b1 is a list with Animal or its parents or Object class, I don't know what exactly b1 has . What happens if b1 is a list of Object ? Can List<Animal> result = new ArrayList<Object> ? No. Never assign different object type for generic. Correction : List<? super Animal> result = .... Because the return type is List<T> and b is List<T> . T is <? super Animal>.


case 2.3
Programmer says List<? extends Animal> b1 = new ArrayList<Animal>();
List<? extends Animal> a1 = new ArrayList<Bird>();
backwards(a1, b1) //won't compile
Compiler yells: Hey, b1 may be a list of Dog, Cat, Bird, while its counterpart a1 must be a list of Dog (or its subtype...) according.
List<? extends Animal> result = backwards(a1, b1) //won't compile
List<Animal> result = backwards(a1,b1) //won't compile.
The problem is in backwards.
Reason:
1. When b1 is list of Dog, a1 must be a list of Dog or subtypes.
2. When b1 is a list of Bird, a1 must be a list of Bird or subtypes.
3. But here b1 can be a list of Dog, a1 can be a list of Cat. I won't compile.


case 2.4 static <T> List<T> backwards (List< ? extends T> a ) { ..... }
Compiler advices : unlike case 2.3, my argument a does not have a counterpart.
In case 2.3, treat a and b as husband and wife. If the wife, b takes a Dog object, the husband takes a Dog or its subtype Object. If the wife takes a Cat, the husband takes a Cat (or subtype).
In case 2.4, treat a as a single man that is not controlled by his wife. Since <? extends T> means a type of T or T-subtype, a can take anything and the compile will promise you to return a list of that type.
Unlike case 1, Collection < ? extends E> is bound by E or subtype declared by ArrayList class, the T is not bound by anything at all. So, in this case, List<? extends T> is the same as List<T>

Therefore, List<? extends Animal> input1= new ArrayList<Animal>();
List<? super Animal > result = backwards( input1); //compiles, because List<? extends Animal> input1 is treated as List<Animal> input1
List<? extends Animal > result = backwards( input1); //compiles
List<Animal> result = backwards(input1); //compiles

correct me if I am wrong in those examples.
I think in the exam, if you are given a method with return type List<T> and T is generic, never choose those choices with a concrete type on the left hand side likes List<Object> o = method(); or List<Animal> a = method() ...



 
Helen Ma
Ranch Hand
Posts: 451
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
In conclusion, in the exam if you are given a return type and input parameters that involves generic parameters, here are two things you may consider:
1. What return type your method will be depending on the input parameter type?
2. What type you can assign the return type from your method.

For example, you are given <T> List<? extends T> methodA( List<T>) {.... }. Make sure you input List<T> and will return List of T or T-subtype depending how the code provided inside the method.
For another example, you are given <T> List<? super T> methodA (List <? super T>). Whatever List element type T you pass in, it will return List<? super T>.


How about assigning a variable to the method output? In order to do that, the partial type hierarchy is considered. It is just like inheritance. eg Object() o = new Animal();
Here are some examples:
Object a = new List();
List<?> a1 = null; List a2 = al; // List is a parent of List<?>
List< ? extends Number> a3 = null; a2 = a3; // List is a parent of List <? extends Number> and so on...
List <? extends Integer> a4 = null ; a3 = a4;
List<? super Integer> a5 = null ;
List <? super Number> a6 = null ; a5 = a6 // List <? super Integer> is a parent of List<? super Number> and so on....
a1 = a5 // List<?> is a parent of List <? super Integer>


Therefore , if your methodA returns List< ? super Animal> , you can write this: List<? super Animal> a = methodA(...) ; or List< ?> a = methodA(...); or List a = methodA(...);



Google A Programmer's Guide to Java SCJP Certification A complete primer 3 rd editon by Mughal and Rasmussen. You may be able to open the pdf book. Read P 678 Figure 14.5
You will see the Partial Type Hierarchy
 
  • Post Reply
  • Bookmark Topic Watch Topic
  • New Topic