I was trying to solve a problem where I am not able to understand part of the answer.
Following is the class BackLister
:
public class BackLister {
// INSERT HERE
{
List<T> output = new LinkedList<T>();
for (T t : input)
output.add(0, t);
return output;
}
}
The question asks which can be inserted at // INSERT HERE
in the BackLister
class to compile and run without error?
Following are the options:
A. public static <T> List<T> backwards(List<T> input)
B. public static <T> List<T> backwards(List<? extends T> input)
C. public static <T> List<T> backwards(List<? super T> input)
D. public static <T> List<? extends T> backwards(List<T> input)
E. public static <T> List<? super T> backwards(List<T> input)
F. public static <? extends T> List<T> backwards(List<T> input)
G. public static <? super T> List<T> backwards(List<T> input)
I understand that that A and B are correct, as for for (T t : input)
to work the elements in input
should be of type T
or subtype of T
.
But I am not able to understand why D
and E
options are correct?
I understand the following:
public static <T> List<? extends T> backwards(List<T> input)
means that the return type should be a List
of T
or subclass
of T
. public static <T> List<? super T> backwards(List<T> input)
means that the return type should be a List
of T
or
superclass of T
.Could somebody help me understand it?
There is difference between each of them and I'm going to explain most of them. Let's start with our example. I use this class hierarchy:
class Food {}
class Apple extends Food {}
class Orange extends Food {}
class RedApple extends Apple {}
List<Food> listFood = new ArrayList<>();
List<Apple> listApple = new ArrayList<>();
List<Orange> listOrange = new ArrayList<>();
List<RedApple> listRedApple = new ArrayList<>();
ow start with first one:
A. public static <T> List<T> backwards(List<T> input)
This method will only accept List<T>
and return List<T>
and you can not send listApple
and return listRedApple
. (however your return list can contain RedApple
because it extends Apple
but type of list must be List<Apple>
and nothing else)
B. public static <T> List<T> backwards(List<? extends T> input)
You can send listRedApple
and return listApple
but you know that listRedApple
is "? extend Apple" so in the method body java recognize T as Apple. Then if you use can add elements in listRedApple
which sent as argument, you can add Apple
in listRedApple
which is not true!!! so compiler avoid it and give compile error. In B you can only read elements (and get it as T) but you can not add anything to it.
C. public static <T> List<T> backwards(List<? super T> input)
You can send listApple
and then in method body you can add anything extends Apple
because compiler see T as Apple
and in a list of anything which is super of T, you can add anything extends Apple
.
However this time, you can not read anything because you don't know its type except you get it as Object
. (It is a list of "? super T")
As you see here there is difference between ? super and ? extend. one of them give you write access and other give you read access. This is the real use of wildcard.
D. public static <T> List<? extends T> backwards(List<T> input)
E. public static <T> List<? super T> backwards(List<T> input)
If you send listApple
then you return List<? extends Apple>
but you can's assign it to any of listFood
or listApple
or listRedApple
because List<? extends Apple>
maybe contain Apple
or RedApple
or something else and we can't assign it to any List<T>
because then we can add T
to that list and maybe T
and ? extends T
is not same. this is same for both D
and E
. You can assign it to List<? extends Apple>
for 'E and List<? super Apple>
for D
and send them to a method which need them as parameter.
F. public static <? extends T> List<T> backwards(List<T> input)
G. public static <? super T> List<T> backwards(List<T> input)
Give compile errors because wildcard can not used like this.
I hope this help you.
If something is wrong, any comment is appreciated.
Options D
and E
are valid because there exist a super-subtype relationships among generic types that allow you to define a larger set of types that the method can accept or return.
Consequently, the following is valid (D):
public static <T> List<? extends T> backwards(List<T> input) {
return List.of();
}
// there exist a super-subtype relationships among List<? extends Number> and List<Long>
List<? extends Number> list = backwards(List.<Long>of(1L, 2L));
because the type Long
is a member of the type family that the wildcard ? extends Number
denotes (the family of types that are subtypes of Number
and the type Number
itself).
The next code snippet is also valid (E):
public static <T> List<? super T> backwards(List<T> input) {
return List.of();
}
List<? super Long> ints = backwards(List.<Long>of(1L, 2L));
because the type Long
is a member of the type family that the wildcard ? super Long
denotes (the family of types that are supertypes of Long
and the type Long
itself).
So, your understanding is correct.
Further reading.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With