Difference for <? super/extends String> in method and variable declaration - java

Given:
import java.util.*;
public class Hancock {
//insert code here
list.add("foo");
}
}
Which two code fragments, inserted independently at line 5, will compile without warnings? (Choose two)
A. public void addString(List list) {
B. public void addString(List<String> list) {
C. public void addString(List<? super String> list) {
D. public void addString(List<? extends String> list) {
Correct answers are B & C.
Answers A and B are quite clear for me. For the answers C & D i know which way the inheritence is going, however i cannot understand why answer D does not compile in Eclipse while all others do (A with warrning about generic, B & C without warrings).
Error in Eclipse for answer D is The method add(capture#1-of ? extends String) in the type List<capture#1-of ? extends String> is not applicable for the arguments (String).
On the other hand this compiles:
public void addString() {
List<? extends String> list1 = new ArrayList<String>();
List<? super String> list2 = new ArrayList<String>();
}
Why? Why <? super String> does not compile in method declaration while it does compile in variable declaration.
I know that String is final class and cannot be extended by any other class but that does not explain to me what is going on here.

First, let's see answer C:
public void addString(List<? super String> list) {
list.add("foo");
}
This method declaration says that you will be allowed to pass List objects which are parametrized by some super class of String, for example String or Object. So:
If you pass List<String> the list.add("foo") will be perfectly valid.
If you pass List<Object> the list.add("foo") will be perfectly valid, because "foo" is a String (and you can add a String to a List<Object>).
This means that answer C is correct.
Lets now see answer D.
If you have a method declaration like this:
public void addString(List<? extends String> list) {
}
this means that you will be able to pass List objects parametrized by some unknown subtype of String. So, when you do list.add("foo"); the compiler won't be aware if the provided object has a type that matches the unknown subtype of String and therefore raises a compile-time error.
When you have:
public void addString() {
List<? extends String> list1 = new ArrayList<String>();
List<? super String> list2 = new ArrayList<String>();
}
This fragment compiles fine, because list1 is defined to hold List objects that are of some unknown subtype of String, including the String itself, which is why it's valid.
The problem is that you won't be able to add anything, except null.
As for list2, the variable can hold List objects which are parametrized by some super-type of String, including the String itself.
More info:
What is PESC?
What is the difference between super and extends in Java wildcards?

Firstly, generics don't care that String is final. They work the same way for final and non-final classes.
With that in mind, it should be apparent why D is not allowed - if it was, you could do this:
void test() {
List<Integer> integers = new ArrayList<Integer>();
addADouble(integers);
int a = integers.get(0); // ????
}
void addADouble(List<? extends Number> list) {
list.add(new Double(5.0));
}
List<? extends Number> is a "List of something that extends Number, but you don't know exactly what it's a List of." - it might be a List<Double>, or a List<Integer>, or a List<Number>, or a List<YourCustomSubclassOfNumber>, so you can't add anything to it because you don't know if it's the right type.

Related

Java Generics List

Following is an example of Generics with wild card
public static void printListItems(List<object> list) {
for (Object listItem : list)
System.out.println(listItem);
}
In this example we want to print list items of any type but it can’t print List<Integer>, List<String> etc. because they are not subtypes of List<Object>. This problem can be solved using unbounded wildcard.
public static void printListItems(List<?> list) {
for (Object listItem : list)
System.out.println(listItem);
}
I read this above code in Java tutorial. For the first example , it says it cannot work because List<String> is not sublass of List<Object>.
Then why it is so that in the second example the for loop is working with taking listItem as dataType of Object and iterating through List<String> elements.
You are running into a false contradiction because you are equating the relationship between List<String> and List<Object> to the relationship between String and Object.
In the first example, the compiler will yell at you if you attempt to call printListItems with a List<String>, because List<String> does not extend List<Object>.
In other words: A list of strings cannot be treated as a list of objects, because (according to the type system) a list of strings is not a list of objects.
In the second example, the compiler will not yell at you, because you are treating the elements of the List, which are Strings, as Objects. Unlike the case with the lists, String does extend Object.
In other words: A string can be treated as an object, because (according to the type system) a string is an object.
when the type-declaration of generic class contain wildcard character, its subclass type can be extend in two dimension.
E.g. Collection<?extends Number>
its subclass type can be extend in dimension Collection, for List and Set are all subclass type of Collection so List<? extends Number> and Set<? extends Number>are all subclass type of Collection<? extends Number>
its subclass type can be extend in dimension Number, because Integer 、Double are all subclass type of Number so Collection<Double> and Collection<Integer> are all subclass of Collection<? extends Number>
about your second, as Andreas explained in comment, List is equal to List<? extend Object> and based on the second dimension extend List<String> is **subclass** of List<?>
What you've done is not generic. This is a generic function
public static <T> void printListItems(List<T> list) {
for (T listItem : list) {
System.out.println(listItem);
}
}
Example:
import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
public class Main {
public static <T> void printListItems(List<T> list) {
for (T listItem : list) {
System.out.println(listItem);
}
}
// arguments are passed using the text field below this editor
public static void main(String[] args) {
List<Integer> l = new ArrayList<>(Arrays.asList(1,2,3,4,5));
List<String> s = new ArrayList<>(Arrays.asList("Hello", "World"));
printListItems(l);
printListItems(s);
}
}
Output
1
2
3
4
5
Hello
World

using generic class as a list [duplicate]

Why this is wrong:
Class<? extends Number> type = Integer.class;
ArrayList<type> = new ArrayList<>();
?
Is there no way to instantiate a class of a specific type given a class object?
Obviously I would never do that directly, that is just an example to show what is needed. In the actual code I need I don't know the name of the type. For example
public void createAList(Class<? extends Number> type)
{
ArrayList<type> toReturn = new ArrayList<>();
return toReturn;
}
<T extends Number> ArrayList<T> createAList(Class<T> type)
{
ArrayList<T> toReturn = new ArrayList<>();
return toReturn;
}
ArrayList<Integer> intList = createAList(Integer.class);
That's not how you use generics. You don't use a Class object, you use the class name directly in your code.
ArrayList<Integer> = new ArrayList<>();
Feel the difference between java Class (which actually generic too) object and class name.
You should use class name specifying generic type.
ArrayList<Number> = new ArrayList<>();
// ArrayList<Number.class> = new ArrayList<>(); <- WRONG
UPD:
Use this approach if you'll know type only in runtime:
public <T extends Number> void createAList(Class<T> type) {
ArrayList<T> toReturn = new ArrayList<>();
return toReturn;
}
An ArrayList<> has to have a specific type it holds . You can put objects of that type or any sub-type in it though.
So use
List<Number> = new ArrayList<Number>();
and you can put Integers in it
Notice how I used the interface on the left and the class on the right of the equal sign. That's a best practice sort of thing.
If you want a list that will just hold Integer (as per your example) the answer by #irreputable is your best bet. This answer will hold Integer but not just Integer.
Taken literally, the other answers' suggestions of how to implement createAList are ignoring something important: due to type erasure, such a method is pointless.
Given you want a List<? extends Number>, you can just write this:
List<? extends Number> lst = new ArrayList<>();
If you just wanted a List<?>, you could write:
List<?> lst = new ArrayList<>();
If you were in the scope of a type parameter T and wanted a List<T>, you could write:
List<T> lst = new ArrayList<>();
Notice that a Class object has nothing to do with these constructor calls, just like the methods in the other answers. That's because at runtime, the ArrayList instance doesn't know or care about whatever generic type its references had at runtime.
You don't even need to pass in an argument:
public <T extends Number> ArrayList<T> createAList () {
return new ArrayList<T>();
}
Though you may need to explicitly specify the type parameter when calling:
ArrayList<Integer> intList = this.<Integer>createAList();
ArrayList<type> = new ArrayList<>();
this line is wrong. First, you missed identifier (variable name) here; Second, you mixed the concepts of "type" and "class". You can delcare
ArrayList<Integer> list = new ArrayList<>();
But according to yours, type = Integer.class. Obviously, Integer is not equivalent to Integer.class. Similarly you can't have Integer.class i = 1; The former one is a "type", the second one is a "Class" object.
You can create a generic method:
public <T extends Number> List<T> createAList (Class<T> type) {
return new ArrayList<T>();
}

Why can't I add to a List<? extends String> in Java?

In the snippet below, adding "Hulooo" to the list generates a compiler error since String doesnt extend String.
However, typecasting an ArrayList of strings works. But typecasting an ArrayList of objects doesn't work.
Can someone explain why this is happening?
What applies to String(in this context) that doesn't apply to Object?
public static void takeList(List<? extends String> list)
{
list.add("Hulloo");//ERROR
list=new ArrayList<String>();
list=new ArrayList<Object>();//ERROR
}
The question mark ? is a so-called wild-card operator. List<? extends String> means: any type List<T> where T is String or a sub-type of String. String is a final class, and thus there are no sub-types of String, but the compiler does not look at this. And thus, the compiler assumes that the list could be a list of some sub-type of String, and it would not be possible to add Strings to such a list.
Let's simulate your example with a non-final class, A, which has a sub-class, B:
class A {
// empty
}
class B extends A {
void f();
}
And now consider the equivalent to your method:
public static void takeList(List<? extends A> list) {
list.add(new A());
}
This will not compile, for the following reason. Suppose you call this method as follows:
List<B> myList = new ArrayList<>(); // line 1
takeList(myList); // line 2
B element = myList.get(0); // line 3
B.f(); // line 4
Since B is a sub-class of A the function call in line 2 is legal. But the method takeList adds an A to the list which is not a B. An then the list of Bs contains an element which is not a B, and line 3 and 4 break down.
The type system is there to prevent typing errors, so if there is one scenario where you could add an object of the wrong type to a list, the type system must forbid it.

Java Generics: How does method inference work when wildcard is being used in the method parameters?

Supposedy i have the following:
class x {
public static void main(String [] args) {
List <?> a = new LinkedList<Object>();
List <? extends Object> b = new LinkedList<Object>();
List <? super Object> c = new LinkedList<Object>();
abc(a, "Hello"); // (1) Error
abc(b, "Hello"); // (2) Error
abc(c, "Hello"); // (3) ok
def(b); // (4) ok
// Showing inference at work
Integer[] a = {10, 20, 30}; // (5)
T is inferred to be ? extends Object
Method signature: ppp(? extends Object, ? extends Object[])
Method call signature: ppp(String, Integer[]);
ppp("Hello", a); // ok
}
static <T> void abc(List<T> a, T b) {}
static <T> void def(List<T> a) {}
static <T> void ppp(T t1, T[] t2){}
}
To begin with, look at clause 5 showing inference at work. Now clause 5 section is a working section.
If that is what it is, then why does clause (1) & (2) have errors?
From my view, all these 3 methods calling have the same inference generated since no actual type parameters is used on the abc method call.
method parameter <T> abc (List <T> a, T b>)
inferred <Object> abc (List <Object>, Object) // (4)
Please bear in mind, method abc() and def() is my method. Compiler doesn't know what i want to do with the List in this method. I might just print the list size or might not even do anything at all as shown above. So there is no get or set involved here.
CONTINUATION -->
This is getting very confusing for me.
class y {
public static void main(String [] args) {
List <Integer> a = new LinkedList<Integer>();
List <Object> b = new LinkedList<Object>();
ppp("Hello", new Integer(1)); // (20) ok
qqq("Hello", a); // (21) error
qqq("Hello", b); // (22) ok
}
static <T> void ppp(T t1, T t2) {}
static <T> void qqq(T t1, List <T> t2) {}
}
Note that clause 21 is the same as clause 20 except 2nd parameter is being made to be a List instead of Integer.
Clause 20 is ok cos' T is inferred to be Object.
Clause 22 is ok. Same reason as clause 20.
Clause 21 failed? T could also be inferred to be Object too - would work too?
The hard thing about the wildcard is to realize ? extends Foo does not mean "anything that extends Foo", but instead it means "some specific type that extends Foo". And since you are outside that definition, you have no way to know which specific sub-type of Foo it is.
Update:
As I said, it's complicated. Here are some comments on your code.
// a list of some specific type, and you don't know what type that is.
// it's a sub-type ob Object, yes, which means that you can do
// Object foo = a.get(0); , but the compiler has no way of knowing
// whether it's a String so you can't pass in a String
List <?> a = new LinkedList<Object>();
// same here. '?' and '? extends Object' are equivalent
List <? extends Object> b = new LinkedList<Object>();
// this is a list of Objects and superclasses thereof.
// since there are no superclasses of Object, this is equivalent to
// List<Object>. And through inheritance, a String is an Object, so
// you can pass it in.
List <? super Object> c = new LinkedList<Object>();
Update 2:
The problem here is that you are dealing with fixed, but unresolveable variables.
// you can pass in a List<String> and a String,
// but if you pass in a List<?>, the compiler has no idea what
// '?' is and just can't substitute 'String'.
// 'T' doesn't help you here, because 'T' can't match both
// '?' and 'String'.
static <T> void abc(List<T> a, T b) {}
// this works because 'T' substitutes '?' and doesn't have to worry
// about a 2nd parameter
static <T> void def(List<T> a) {}
Read this question, it might shed some light on the problem:
What is PECS (Producer Extends Consumer Super)?
You've set up a bit of a straw man by creating a LinkedList<Object> in each case. That can make it difficult to see the problem. What you have to remember is that when the compiler gets to those method invocations, it doesn't know that you created a LinkedList<Object>. It could be a LinkedList<Integer>, for example.
So let's look at your code with more interesting initializations:
List<Integer> integers = new LinkedList<Integer>();
List <?> a = integers;
List <? extends Object> b = integers;
List <? super Object> c = new LinkedList<Object>();
//INVALID. T maps to a type that could be Object OR anything else. "Hello"
//would only be type-assignable to T if T represented String, Object, CharSequence,
//Serializable, or Comparable
abc(a, "Hello");
//INVALID. T maps to a type that could be Object OR anything else. "Hello"
//would only be type-assignable to T if T represented String, Object, CharSequence,
//Serializable, or Comparable
abc(b, "Hello");
//VALID. T maps to an unknown super type of Object (which can only be Object itself)
//since String is already type-assignable to Object, it is of course guaranteed to be
//type-assignable to any of Object's super types.
abc(c, "Hello");
Integer i1 = integers.get(0);
Integer i2 = integers.get(1);
It doesn't take much to see that if the implementation of abc was this:
//a perfectly valid implementation
static <T> void abc(List<T> a, T b) {
a.add(b);
}
That you would get a ClassCastException when initializing i1.
From my view, all these 3 methods calling has the following inference generated since no actual type parameters is used on the abc static method call.
method parameter <T> abc (List <T> a, T b>)
inferred <Object> abc (List <Object>, Object) // (4)
This is categorically wrong. It is not inferred that T is Object in any of your examples, not even in the case of ? super Object. T is resolved to the capture of a, and unless you can assign a String to that capture (as is the case when it's ? super Object) you will have a type error.
Edit #1
Regarding your update (I've replaced your generic array with a List<T> since generic arrays needlessly cloud the issue):
// Showing inference at work
List<Integer> a = Arrays.asList(10, 20, 30); // (5)
T is inferred to be ? extends Object
Method signature: ppp(? extends Object, List<? extends Object>)
Method call signature: ppp(String, List<Integer>);
ppp("Hello", a); // ok
This is not correct. The crucial mistake you're making is here:
Method signature: ppp(? extends Object, List<? extends Object>)
This is not at all what the capture engine does or should translate your invocation into. It resolves T as <? extends Object> but as one specific capture of <? extends Object>. Let's call it capture-1-of<? extends Object>. Thus your method must be like this:
Method signature: ppp(capture-1-of<? extends Object>, List<capture-1-of<? extends Object>>)
This means that there is a binding between the two parameters...they must resolve to the same capture. In general it is very difficult to tell the compiler that two things are the same capture. In fact, even this is not a valid invocation of ppp (even though they are clearly the same capture):
List<? extends Integer> myList;
ppp(myList.get(0), myList);
One way we could invoke ppp is through a generic intermediary:
public static <T> void pppCaller(List<T> items) {
ppp(items.get(0), items);
}
pppCaller(myList);
The only sure-fire way you could invoke ppp with a wildcarded list would be to invoke it like this:
List<? extends Integer> myList = new ArrayList<Integer>();
ppp(null, myList);
That's because the null is the only thing that you can assign to anything. On the other hand, if you had this method:
private static <T> void qqq(T item1, T item2) {}
You could indeed invoke it like this:
List<? extends Integer> myList;
qqq(myList.get(0), myList.get(1));
Because in this case, the inference can generalize T to Object. Since List<? extends Integer> is not covariant with List<Object>, it cannot do the same for ppp().
However, what most people do to get around this is to relax their method signature. Instead, declare ppp as the following:
public static <T> ppp(T item, List<? super T> items) {
}
This follows the guidelines that Sean put in his post of "PECS"
If (your method) produces, use extends, if it consumes, use super.
Edit #2
Regarding your latest edit:
public static void main(String [] args) {
List <Integer> a = new LinkedList<Integer>();
qqq("Hello", a); // (21) error
}
static <T> void qqq(T t1, List <T> t2) {}
Object is not a valid inference for T. I think this is something fundamental you're missing, so I'll say it clear:
A List<Integer> is NOT type-assignable to List<Object>
Not at all. If it were, you could do something like this which obviously violates type safety:
List<Integer> myInts = new ArrayList<Integer>();
List<Object> myObjects = myInts; //doesn't compile!
myObjects.add("someString");
Integer firstInt = myInts.get(0); //ClassCastException!
So T cannot be inferred as Object, since it would require assigning a List<Integer> to a variable of type List<Object>.
A wildcard would then needed to induce subtype covariance
I'd rather say "try to simulate" since even after using wild-cards you can't get the same functionality you get for arrays.
Then the question is why clause (3) works and not clause(2) or (1)?
Consider the first declaration:
List <?> a = new LinkedList<Object>();
This declaration effectively says, I really don't know (or care) what kind of element the collection a contains. This effectively shuts you off from "mutating" the collection since you might end up adding elements of type which are not really compatible with a. You can have List<?> a = new ArrayList<String>() but you still won't be able to put anything in it. Basically, in case an add is allowed, the compiler can't guarantee the type safety of the collection.
List <? extends Object> b = new LinkedList<Object>();
Here you say b is a collection which contains elements which extend an Object. What kind of element, you don't know. This again as per the previous discussion doesn't allow you to add anything since you could end up compromising type safety.
List <? super Object> c = new LinkedList<Object>();
Here you say, c is a collection which contains elements of type Object and it's super-classes or in other words, at least an Object. Since each reference type in Java is assignment compatible with Object, it works in the third case.
Integer[] is a subtype of Object[], however List<Integer> is not a subtype of List<Object>. This is quite confusing; arrays are more primitive and should be avoided.
If a method parameter type is T[], it accepts all S[] where S is a subtype of T.
To do this with List, the type should be List<? extends T>. It accepts all List<S> where S is a subtype of T.
List<?> a means that a holds a specific type, which is unknown. Consider this more complete example:
List<Float> floats = new ArrayList<Float>();
List<?> a = floats; /* Alias "floats" as "a" */
abc(a, "Hello"); /* This won't compile... */
float f = floats.get(0); /* .. if it did, you'd get a ClassCastException */
static <T> abc(List<T> a, T b) {
a.add(b); /* Absolutely no problem here. */
}
List<? extends Object> means essentially the same thing as List<?>, and would cause the same error.
List<? super Object> means that the list holds a specific, but unknown super-type of Object, and the parameterized method can accept any object that is-a Object for the second parameter. While the method invocation is type-safe, attempting to assign an unsafe value to c will cause an error:
List<Number> numbers = new ArrayList<Number>();
List<? super Object> a = numbers; /* This won't compile... */
abc(a, "Hello");
Number n = numbers.get(0); /* ...if it did, you'd get a ClassCastException */

How to iterate over a mixed list using foreach?

I'm wondering how to iterate over a List with mixed contents using foreach. See the example code below.
public class GenericsForeach {
class A {
void methodA() {
System.out.println(getClass().getSimpleName() + ": A");
}
}
class B extends A {
void methodB() {
System.out.println(getClass().getSimpleName() + ": B");
}
}
void test() {
List<A> listOfA = new ArrayList<A>();
listOfA.add(new A());
List<B> listOfB = new ArrayList<B>();
listOfB.add(new B());
List<? super A> mixed = new ArrayList<A>();
mixed.addAll(listOfA);
mixed.addAll(listOfB);
Iterator<? super A> it = mixed.iterator();
while (it.hasNext()) {
A item = (A) it.next();
item.methodA();
}
// XXX: this does not work
// for (A item : mixed) {
// item.methodA();
// }
}
public static void main(String[] args) {
new GenericsForeach().test();
}
}
I construct two lists with different, but related, content types A and B (B extends A). I add the two lists to a 'mixed' list, which I declare to contain <? super A> types. Since this mixed list is 'consuming' items of type A (or B) I applied Bloch's PECS rule (Producer Extends, Consumer Super) to determine that I need <? super A> here.
So far, so good. But now when I want to iterate over this mixed list, I can only seem to do it with an Iterator<? super A>, and a cast A item = (A) it.next(). When I try to use a foreach loop (see commented-out code), no joy:
Type mismatch: cannot convert from element type capture#8-of ? super GenericsForeach.A to GenericsForeach.A
Eclipse even helpfully offers to
Change type of 'item' to '? super A'
but this results in disaster:
for (? super A item : mixed) {
item.methodA();
}
So I don't know. Eclipse doesn't seem to know. Does anybody else here know if this is possible, and if it's not, why not?
You want just List<A> for mixed. My reasoning:
you want to be able to add items which are of type A, so it can't be List<? extends A> - that would include List<B>, which you can't add an A to.
you want to be able to guarantee that items which you fetch are of type A, so it can't be List<? super A> as that could be a List<Object> containing non-A elements.
So you end up with:
List<A> mixed = new ArrayList<A>();
mixed.addAll(listOfA);
mixed.addAll(listOfB);
for (A item : mixed) {
item.methodA();
}
Everyone here is correct. You want use List<A>.
But generics and assignments can be confusing, so a little more explanation is in order.
First, the problem you may have found is that you can't do this: List<A> = new List<B>(). The compiler won't let you assign a sub-type in to a super-type listing using generics. This is a little confusing, but it prevents problems with type mis-matches. (More detail can be found here: http://java.sun.com/docs/books/tutorial/java/generics/subtyping.html.) The correct terminology for this is List<? extends A> = new List<B>(). This tells the compiler that your assignment is legal.
At the same time, this syntax can confuse you to believing that <? extends A> means that all elements in this variable extend A. This isn't true - the syntax just a way to inform the compiler of legal assignments.
So, you want to use List<A> = new List<A> and then assign the elements to List<A> using addAll(). This is legal because the method addAll checks to make sure each element is valid before pushing it to the collection.

Categories

Resources