Java Generic ? extends String - java

While I was going through some generics question I came across this example. Will you please explain why list.add("foo") and list = new ArrayList<Object>() contain compailation issues?
In my understanding List of ? extends String means "List of Something which extends String", but String is final ? can only be String. In list.add() we are adding "foo" which is a string. Then why this compilation issue?
public class Generics {
public static void main(String[] args) {
}
public static void takelist(List<? extends String> list){
list.add("foo"); //-- > error
/*
* The method add(capture#1-of ? extends String) in the
* type List<capture#1-of ? extends String> is not applicable
* for the arguments (String)
*/
list = new ArrayList<Object>();
/*
* Type mismatch: cannot convert from ArrayList<Object> to List<? extends String>
*/
list = new ArrayList<String>();
Object o = list;
}
}

For starters, the java.lang.String class is final, meaning nothing can extend it. So there is no class which could satisfy the generic requirement ? extends String.
I believe this problem will cause all of the compiler errors/warnings which you are seeing.
list.add("foo"); // String "foo" does not extend String
list = new ArrayList<Object>(); // list cannot hold Object which does not extend String

It is true what you say. String is final. And so you can reason that List<? extends String> can only be list of string.
But the compiler isn't going to make that kind of analysis. That is to say, the compiler will not assess the final or non-final nature of String (see comments). The compiler will only let you put null into your list.
You can pull stuff out though.
String s = list.get(0);

While String is final, this information is not used.
And in fact, with Java 9, it may no longer be final (rumor has it that Java 9 may finally get different more efficient String types).
Without knowing it is final, List<? extends String> could be e.g. a List<EmptyString> of strings that must be empty.
void appendTo(List<? extends String> l) {
l.append("nonempty");
}
appendTo(new ArrayList<EmptyStrings>());
would yield a violation of the generic type.
As a rule of thumb always use:
? extends Type for input collections (get is safe)
? super Type for output collections (put is safe)
Type (or maybe a <T>) for input and output collections (get and put are safe, but the least permissive).
I.e. this is fine:
void appendTo(List<? super String> l) {
l.append("nonempty");
}
appendTo(new ArrayList<Object>());

You have already mentioned that String is a final type and therefore there is no point in repeating this fact. The point that is important to note is that none of the following Lists allows adding an element:
List<?> which is a List of anything.
List<? extends SomeType> which is a List of anything that extends SomeType.
Let's understand it with an example.
The List<? extends Number> could be List<Number> or List<Integer> or List<Double> etc. or even a List of some other type that hasn't been defined yet. Since you can not add any type of Number to a List<Integer> or any type of Number to a List<Double> etc., Java does not allow it.
Just for the sake of completeness, let's talk about List<? super Integer> which is List of anything that is a super/parent type of Integer. Will the following compile?
Object obj = 10.5;
list.add(obj);
As you can guess, of course NOT.
What about the following?
Object obj = 10.5;
list.add((Integer) obj);
Again, as you can guess, indeed it will compile but it will throw ClassCastException at runtime. The question is: why did not Java stop us in the first place by failing the compilation itself? The answer is Trust. When you cast something, the compiler trusts that you already understand the cast.
So, the following compiles and runs successfully:
Object obj = 10;
list.add((Integer) obj);
list.add(20);

Related

Generics Java Wildcards and Subtyping

I am reading on Generics in Java atm, and things go a little slow would love some help. I read from Oracles own database:
https://docs.oracle.com/javase/tutorial/java/generics/subtyping.html
At the bottom we can see List<Integer> is a subtype of List<? extends Number>
I also came across this stackoverflow question:
Java Generic type : difference between List <? extends Number> and List <T extends Number>
Which says in one answer: this is true:
((List<Integer>)list).add((int) s);
I have verified it, so that's ok. But I don't understand It completely.
What if the Wildcard is the Short class and I add a number higher than 2^15-1(=32767) Shouldn't it give error?
I even tried something like this and it works fine:
import java.util.*;
class CastingWildcard{
public static void main(String[] args){
List<? extends Number> list = new ArrayList<Short>();
int s=32770;
((List<Integer>)list).add((int) s);
System.out.println(list.get(0));
}
}
To sum up: Why Can I cast List<? extends Number> to List<Integer> when the wildcard could be Short, and even Byte, which also extends Number?
The cast makes the compiler ignore the fact, that the types may not be assignable.
At runtime the type parameters are unimportant, see type erasure.
The ArrayList internally stores the content in a Object[] array, which means you can add any reference type to the list object, if you "abuse" casting.
You may get a exception when you retrieve a Object though, since there's a cast hidden in the get statement.
Example:
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3));
List<String> list2 = (List) list;
list2.add("Hello World");
Integer i = list.get(0); // works
String s = list2.get(3); // works
s = list2.get(1); // ClassCastException
i = list.get(3); // ClassCastException
You can cast an object to anything you want, but it might fail at runtime. However since generics information isn't present during runtime, your code becomes essentially ((List)list).add(s);. At that point list will take any object, not just a Number. Generics can help you avoid casts and keep type safety during compile time, but during runtime they don't matter anymore.

generics ? extends T - can't use T [duplicate]

I have a Java question about generics. I declared a generic list:
List<? extends MyType> listOfMyType;
Then in some method I try instantiate and add items to that list:
listOfMyType = new ArrayList<MyType>();
listOfMyType.add(myTypeInstance);
Where myTypeInstance is just an object of type MyType; it won't compile. It says:
The method add(capture#3-of ? extends
MyType) in the type List<capture#3-of
? extends MyType> is not applicable
for the arguments (MyType)
Any idea?
You cannot do a "put" with extends . Look at Generics - Get and Put rule.
Consider:
class MySubType extends MyType {
}
List<MySubType> subtypeList = new ArrayList<MySubType>();
List<? extends MyType> list = subtypeList;
list.add(new MyType());
MySubType sub = subtypeList.get(0);
sub now contains a MyType which is very wrong.
You shouldn't need to use the wildcard capture syntax in your case, simply declaring
List<MyType> listOfMytype;
should be enough. If you want to know exactly why, the Java Generics Tutorial has more than you would ever want to know about the esoteric craziness of Java Generics. Page 20 addresses your specific case.
As for why add with the wildcard capture does not work, it is because the compiler can't determine exactly what subclass of MyType the list will be in every case, so the compiler emits an error.
There is a similar thread here:
How can elements be added to a wildcard generic collection?
To get an idea of how generics works check out this example:
List<SubFoo> sfoo = new ArrayList<SubFoo>();
List<Foo> foo;
List<? extends Foo> tmp;
tmp = sfoo;
foo = (List<Foo>) tmp;
The thing is, that wasn't designed for local/member variables, but for function signatures, that's why it's so ass-backwards.
I dont know if this will really help you, but this is something I had to use while calling a generic method of Spring Framework and wanting to return also a generic list:
public <T> List<T> findAll(String tableName,Class<?> table) {
String sql = "SELECT * FROM "+ tableName ;
List<?> entities = getSimpleJdbcTemplate().query(sql,
ParameterizedBeanPropertyRowMapper.newInstance(table));
return (List<T>) entities;
}
Seems the parametrization needs you to use the ? sign in the list to receive the results and then cast the list to the expected return type.
Iam still dazzled by generics...

java generics bounds

I have the following code:
public <T extends SomeObject> long doSomething(T someObject){
List<? extends SomeObject> l = new LinkedList<>();
l.add(someObject);
}
this causes a compilation error - telling me that there is no suitable methods found: add(T),
why is that?
If l accept things that extends SomeObject shouldn't it accept someObject as it bounds to extend SomeObject?
List<? extends SomeObject> l
What do you mean by that? Of course it will generate an error.
Take this example :SomeObject is Fruit, you have 2 derived classes Apple and Orange
Your list what will it contain? Apples or Oranges? The compiler cannot tell. So it generates error.
If you replace List<? extends SomeObject> l with List<SomeObject> l. Then this will work because Apple and Orange are both Fruit.
I would advise you to use this statement:
List<T> l = new LinkedList<T>();
This is no less type-safe then
List<SomeObject> l = new LinkedList<SomeObject>();
and additionally gives you an opportunity to get objects of type T from the list without casting. T is already SomeObject so no casting required to call methods of SomeObject on T.
And all that with less typing!
Back to the problem.
First thing to note is that wildcard type "?" means unknown, this is important.
You may, however, specify an upper (? extends) or a lower (? super) constraint to it.
You declared a list as "List".
List is known to have objects of SomeObject inside. but! the exact type of objects is unknown.
Compiler can not say if there are instances of "class A extends SomeObject" or instances of "class B extends SomeObject" inside the list.
If you call list.get() it can only say that there will be an object of type SomeObject.
SomeObject obj = list.get(1); // Ok
But inserting an object of any(!) type is unsafe because the actual type of elements in the list is unknown.
You could wonder why wildcard type ever exists.
It is here to lower restriction in type casting that will be too strict otherwise.
Sample
class A { }
class A2 extends A { }
class B <T> {
void change(T a) { .. };
T read() { .. };
}
If there were no wildcards we would not be able to do this: B<A> b = new B<A2>(); - it does not work.
This is because type conversion from B<A> to B<A2> is unsafe.
Why? Let's look (copied from http://en.wikipedia.org/wiki/Generics_in_Java)
List<Integer> ints = new ArrayList<Integer>();
ints.add(2);
List<Number> nums = ints; // valid if List<Integer> were a subtype of List<Number>
nums.add(3.14);
Integer x = ints.get(1); // now 3.14 is assigned to an Integer variable!
What is the solution? Sometimes, we want to do such assignments or pass parameters in a general way!
Wildcard type helps here: B<? extends A> b = new B<A2>();
Method B.void change(T a) is now disabled - this is what your question was about and explained in the first part.
Method B.T read() is still valid and returns A: A a = b.read();. Yes, it returns A2 actually but to the caller of b.read() it's visible as A.
Wildcard types are widely used in Collections Framework.

Help with Java Generics: Cannot use "Object" as argument for "? extends Object"

I have the following code:
import java.util.*;
public class SellTransaction extends Transaction {
private Map<String,? extends Object> origValueMap;
public SellTransaction(Map<String,? extends Object> valueMap) {
super(Transaction.Type.Sell);
assignValues(valueMap);
this.origValueMap=valueMap;
}
public SellTransaction[] splitTransaction(double splitAtQuantity) {
Map<String,? extends Object> valueMapPart1=origValueMap;
valueMapPart1.put(nameMappings[3],(Object)new Double(splitAtQuantity));
Map<String,? extends Object> valueMapPart2=origValueMap;
valueMapPart2.put(nameMappings[3],((Double)origValueMap.get(nameMappings[3]))-splitAtQuantity);
return new SellTransaction[] {new SellTransaction(valueMapPart1),new SellTransaction(valueMapPart2)};
}
}
The code fails to compile when I call valueMapPart1.put and valueMapPart2.put, with the error:
The method put(String, capture#5-of ? extends Object) in the type Map is not applicable for the arguments (String, Object)
I have read on the Internet about generics and wildcards and captures, but I still don't understand what is going wrong. My understanding is that the value of the Map's can be any class that extends Object, which I think might be redundant, because all classes extend Object. And I cannot change the generics to something like ? super Object, because the Map is supplied by some library.
So why is this not compiling? Also, if I try to cast valueMap to Map<String,Object>, the compiler gives me that 'Unchecked conversion' warning.
Thanks!
If the library specifies extends then they are explicitly disallowing put. You should defensively copy before modifying, since they can quite legitimately change their return type to be immutable in a new version. If copying is expensive, then you can try creating a map type that is of type <String, Object> that first queries their map, and then queries some map you create that has your local modifications.
If you do know that their return type is immutable and that you solely own it, then the #SuppressWarnings("unchecked") annotations is a legitimate way to work around the warning, but I would double check those assumptions and comment extensively.
To understand extends vs super, look at it this way.
Since the value can be any type that extends Object, the following is valid.
Map<String, Number> strToNum = new HashMap<String, Number>();
strToNum.put("one", Integer.valueOf(1)); // OK
Map<String, String> strToStr = new HashMap<String, String>();
strToStr.put("one", "1"); // OK
Map<String, ? extends Object> strToUnk = randomBoolean() ? strToNum : strToStr;
strToUnk.put("null", null); // OK. null is an instance of every reference type.
strToUnk.put("two", Integer.valueOf(2)); // NOT OK. strToUnk might be a string to string map
strToUnk.put("two", "2"); // NOT OK. strToUnk might be a string to number map
So put doesn't really work with the extends boundary types.
But it works perfectly well with reading operations like get:
Object value = strToUnk.get("one"); // We don't know whether value is Integer or String, but it is an object (or null).
If you want a map to primarily use with "put" instead of "get", then you can use "super" instead of extends as in:
Map<String, Number> strToNum = new HashMap<String, Number>();
Map<String, Object> strToObj = new HashMap<String, Object>();
Map<String, ? super Number> strToNumBase;
if (randomBoolean()) {
strToNumBase = strToNum;
} else {
strToNumBase = strToObj;
}
// OK. We know that any subclass of Number can be used as values.
strToNumBase.put("two", Double.valueOf(2.0d));
// But now, gets don't work as well.
Number n = strToNumBase.get("one"); // NOT OK.
As far as I know, bounded widecards, i.e. ? extends Number, is not used for variables or fileds. It is commonly used for arguments of method.
Let's first consider a case without generic type.
public void method(List<Number> list) {
}
Example usages:
method(new List<Double>()); // <-- Java compiler complains about this
method(new List<Number>()); // <-- Java compiler is happy with this.
You can only pass a List of Number but not the List of Double to this method even if Double is subclass of Number.
The widecard generic can be used here to tell java compiler that this method can accept any list of subclass of Number.
public void method(List<? extends Number> list) {
}
Example usages:
method(new List<Double>()); // <-- Java compiler is happy with this.
method(new List<Number>()); // <-- Java compiler is happy with this.
However, you will no longer be able to modify the list object, e.g.
public void method(List<? extends Number> list) {
list.add(new Double()); // this is not allowed
}
The above list now have type of "unknown subtype of Number" which can be List, List, List, etc. Adding a Double object to the list of unknown type is certainly unsafe. To illustrate this point, a call to method is
method(new ArrayList<Integer>());
...
public void method(List<? extends Number> list) {
// adding Double to Integer list does not make sense.
list.add(new Double()); // compiler error
}
For variables and fields, you normally don't use bounded widecards, you can do
private Map<String, Object> origValueMap;
...
Map<String, Object> valueMapPart1 = origValueMap;
valueMapPart1.put(nameMappings[3], new Double(splitAtQuantity));
Note: there is no need to cast new Double(splitAtQuantity) to its super type, e.g Number or Object.
This really goes to an old object-oriented gotcha. At first glance, it would seem that a "bag of apples" is a subclass of a "bag of fruit" but it is not. With object-oriented code, you can always use a subclass in place of a superclass (which is called the Liskov Substitution Principle). A bag of apples breaks this because it will not accept an orange whereas a bag of fruit would accept an orange.
In the terms of the question, Collection<?> could be a Collection<Object> (which would accept your Double) or it could be a Collection<Integer> (which would not).

List<? extends MyType>

I have a Java question about generics. I declared a generic list:
List<? extends MyType> listOfMyType;
Then in some method I try instantiate and add items to that list:
listOfMyType = new ArrayList<MyType>();
listOfMyType.add(myTypeInstance);
Where myTypeInstance is just an object of type MyType; it won't compile. It says:
The method add(capture#3-of ? extends
MyType) in the type List<capture#3-of
? extends MyType> is not applicable
for the arguments (MyType)
Any idea?
You cannot do a "put" with extends . Look at Generics - Get and Put rule.
Consider:
class MySubType extends MyType {
}
List<MySubType> subtypeList = new ArrayList<MySubType>();
List<? extends MyType> list = subtypeList;
list.add(new MyType());
MySubType sub = subtypeList.get(0);
sub now contains a MyType which is very wrong.
You shouldn't need to use the wildcard capture syntax in your case, simply declaring
List<MyType> listOfMytype;
should be enough. If you want to know exactly why, the Java Generics Tutorial has more than you would ever want to know about the esoteric craziness of Java Generics. Page 20 addresses your specific case.
As for why add with the wildcard capture does not work, it is because the compiler can't determine exactly what subclass of MyType the list will be in every case, so the compiler emits an error.
There is a similar thread here:
How can elements be added to a wildcard generic collection?
To get an idea of how generics works check out this example:
List<SubFoo> sfoo = new ArrayList<SubFoo>();
List<Foo> foo;
List<? extends Foo> tmp;
tmp = sfoo;
foo = (List<Foo>) tmp;
The thing is, that wasn't designed for local/member variables, but for function signatures, that's why it's so ass-backwards.
I dont know if this will really help you, but this is something I had to use while calling a generic method of Spring Framework and wanting to return also a generic list:
public <T> List<T> findAll(String tableName,Class<?> table) {
String sql = "SELECT * FROM "+ tableName ;
List<?> entities = getSimpleJdbcTemplate().query(sql,
ParameterizedBeanPropertyRowMapper.newInstance(table));
return (List<T>) entities;
}
Seems the parametrization needs you to use the ? sign in the list to receive the results and then cast the list to the expected return type.
Iam still dazzled by generics...

Categories