Unchecked cast warning, clarification needed - java

Suppose we're dealing with the following set up:
A extends B
Main does:
public void doIt() {
List<A> listOfA = new ArrayList<A>();
listOfA.add(new A());
listOfA.add(new A());
C c = new C();
// We know that really, listOfA is going to be modified
// in some way and will be returned to us, thus, it is
// ok to cast result back to what we know it to be
List<A> a = (List<A>) c.doSomethingWithListOfA(listOfA);
}
When C has a method
public List<? extends B> doSomethingWithListOfA(List<? extends B>
listOfObjectsThatExtendB) {
return someFormofListA;
}
The cast of (List) c.doSomethingWithListOfA(listOfA); comes back with - Type safety: Unchecked cast from List<capture#1-of ? extends B> to List<A> warning.
I wonder is it possibly because some other class may also extend B thus making compiler unsure whether in fact is it a list of A or possibly X that extends B?
Other then suppressing the warning, how can one check for this? (unchecked cast)

It's not type-safe because that method could return a list of any type which is a subtype of B -- which may or may no be A.
How about writing this:
public <T extends B> List<T> doSomethingWithListOfA(List<T> listOfObjectsThatExtendB) {
return listOfObjectsThatExtendB;
}
and now casting is no longer needed:
List<A> a = c.doSomethingWithListOfA(listOfA);

Related

If B extends A, one cannot cast List<A> to List<B> (makes sense), but then why can one cast List<? extends A> to List<B>?

If a class B extends class A, one cannot cast List<A> to List<B> (cast 1 below). This makes sense, because if it were allowed, then one could read a B from the list even if it contained none.
However, in the code below, cast 2 from List<? extends A> to List<B> causes a warning. Shouldn't it generate an error for the same reason as before? In the code, the list only contains one object that is an A but not a B, and yet it is in a list deemed List<B>.
package test;
import java.util.LinkedList;
import java.util.List;
public class TypeLowerBoundCasting {
static class A {}
static class B extends A {}
static void method1() {
List<A> listOfAs = new LinkedList<A>();
listOfAs.add(new A());
// List<B> listOfAsAsListOfBs = (List<B>) listOfAs ; // cast 1: compiler error
// B b = listOfAsAsListOfBs.get(0); // that would be bad
}
static void method2() {
LinkedList<A> listOfAs = new LinkedList<A>();
listOfAs.add(new A());
List<? extends A> listOfAExtensions = listOfAs;
List<B> listOfAsAsListOfBs = (List<B>) listOfAExtensions; // cast 2: warning, but compiles
B b = listOfAsAsListOfBs.get(0); // that IS bad; causes a run-time error.
}
public static void main(String[] args) {
method2();
}
}
A List<? extends A> might be a List<B>, so you can cast:
List<? extends A> list = new ArrayList<B>(); // lose type information
List<B> result = (List<B>) list; // regain type information
Similar to how you can do:
Object o = "Hello World!"; // lose type information
String s = (String) o; // regain type information
Unfortunately, the first cast is unchecked. But a cast in that place is still a valid option, as you can see.
But a List<A> can never actually be a List<B> (unless you abuse raw types), because List<A> is not assignable from a List<B> (i.e. List<B> does not 'extend' List<A>) so you can't cast:
List<A> list = new ArrayList<B>(); // Only works with: (List<A>) (List) new ArrayList<B>();

wildcard generics in HashSet constructor

The java HashSet implementation has a constructor:
public HashSet(Collection<? extends E> c) {
map = new HashMap<E,Object>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
Why it is Collection<? extends E> c? This isn't enough: Collection<E> c?
The concept here is called variance (covariance, contravariance).
Let's say you have the following two classes:
class A {}
class B extends A {}
In this case, you can say that an instance of B is an instance of A. In other words, the following code is perfectly valid:
A instance = new B();
Now, generic classes in Java are, by default, invariant. That means that a List<B> is not a List<A>. In other words, the following code will not compile:
List<A> as = new ArrayList<B>(); // error - Type mismatch!
However, if you have an instance of B, sure you can add it to a list of A (because B extends A):
List<A> as = new ArrayList<A>();
as.add(new B());
Now, let's say you have a method that deals with lists of A by consuming its instances:
void printAs(List<A> as) { ... }
It would be tempting to make the following call:
List<B> bs = new ArrayList<B>();
printAs(bs); // error!
However, it won't compile! If you want to make such a call work, you have to make sure that the argument, List<B>, is a subtype of the type expected by the method. This is done by using covariance:
void printAs2(List<? extends A> as) { ... }
List<B> bs = new ArrayList<B>();
printAs2(bs);
Now, this method takes an instance of List<? extends A>, and it is true that List<B> extends List<? extends A>, because B extends A. This is the concept of covariance.
After this introduction, we can go back to the constructor of HashSet you mention:
public HashSet(Collection<? extends E> c) { ... }
What this means is that the following code will work:
HashSet<B> bs = new HashSet<B>();
HashSet<A> as = new HashSet<A>(bs);
It works because HashSet<B> is a HashSet<? extends A>.
If the constructor were declared as HashSet(Collection<E> c), then the second line on the wouldn't compile, because, even if HashSet<E> extends Collection<E>, it is not true that HashSet<B> extends HashSet<A> (invariace).
This is because when HashMap can contain and object that inherits from E, so you want to be able to pass a collection of objects of any type that inherits E, not just E.
If it were Collection, then you wouldn't be able to pass an ArrayList<F>, where F extends E, for example.

Covariance and contravariance for wildcarded types

Can you please explain why it is possible to do:
import java.util.ArrayList;
import java.util.List;
public class Covariance {
class A {
}
class B extends A {
}
class C extends A {
}
public void testSmth() throws Exception {
List<? extends A> la = new ArrayList<A>();
A a = la.get(0);
// la.add(new B()); - doesn't compile
List<? super B> lb = new ArrayList<A>();
// lb.add(new A()); - doesn't compile
lb.add(new B());
Object object = lb.get(0);
}
}
I don't understand, why it is not possible to add something to covariant list la, but it's still possible to add B to contravariant list lb - but not A to lb.
From my point of view, it should be possible to add everything extending A to List. I can see the only reason of not doing that because it is easy to add C to list of B, like
List<B> lst = new ArrayList<B>();
List<? extends A> lstB = lst;
lstB.add(C); // this is valid for <? extends B> but list lst would contain instance of B.
Probably the same is true for contravariance, e.g
List<B> lst = new ArrayList<B>;
List<? super C> lstC = lst;
lstC.add(new C());
Object obj = lstC.get(0);
What I don't understand - why it is not possible to do
B b = lstC.get(0);
It is obvious that on this stage super of C would be class B - Java doesn't allow multiple inheritance.
Also why it prohibits
lstC.add(new B());
it's not clear for me.
To understand what's going on with the super keyword, consider the following:
import java.util.ArrayList;
import java.util.List;
public class Covariance {
class A {
}
class B extends A {
}
class C extends A {
}
class D extends C {}
public void testSmth() throws Exception {
List<? super D> ld = new ArrayList<C>();
}
}
Hopefully this illustrates that ld can be a List of any super type of D, even one that be a subclass of A.
consider
List<? extends A> la = new ArrayList<C>();
it's a list of C. if we could add B to it, that would violate the list type.

Java Generics, return generic extending

Why am i not allowed to do this?
public abstract class A {}
public class B extends A {}
...
public ArrayList<A> foo()
{
return new ArrayList<B>();
}
I changed to public since there are so many people that love to point stupid errors.
Why should i have to write ALL this code. Just to satisfy Java's non-senses?
public List<A> foo()
{
List<A> aList = new ArrayList<A>();
List<B> bList = new ArrayList<B>();
/* fill bList*/
for (B b : bList)
{
aList.add(b);
}
return aList;
}
An ArrayList<B> is not an ArrayList<A>. You can't add any arbitrary A into it, for example. Or as I like to think of it: a bunch of bananas isn't a fruitbowl. When you try to add an apple to a bunch of bananas, it rolls off...
You can use wildcards to make it work though:
public ArrayList<? extends A> foo()
{
return new ArrayList<B>();
}
See the Java Generics FAQ for more details.
EDIT: To answer your specific question of why you need to write all that extra code: you don't. Just create an ArrayList<A> within foo() to start with. There's no need to copy the contents of one list to another.
If you still object to Java's behaviour, what would you want to happen with the following code?
// Doesn't compile, fortunately...
List<String> strings = new List<String>();
List<Object> objects = strings;
objects.add(new Date());
String string = strings.get(0); // Um, it's a Date, not a String...
a) For one thing, function does not exist in Java. Java methods have the format
modifiers <type_parameters[,type_parameter]*>? return_type method_name (
[parameter[,parameter]*]?
) [throws exceptiontype[, exceptiontype]*]{ method_body }
b) Here's how to do it:
public List<? extends A> foo()
{
return new ArrayList<B>();
}
c) I changed the method signature to List. It's bad practice to have implementation types in your class' external API if an appropriate interface exists.
because ArrayList<B>() is not ArrayList<A>. it is not extended from it
B extends A doesn't mean ArrayList<B>() extends ArrayList<A>()

Java Generics (Wildcards)

I have a couple of questions about generic wildcards in Java:
What is the difference between List<? extends T> and List<? super T>?
What is a bounded wildcard and what is an unbounded wildcard?
In your first question, <? extends T> and <? super T> are examples of bounded wildcards. An unbounded wildcard looks like <?>, and basically means <? extends Object>. It loosely means the generic can be any type. A bounded wildcard (<? extends T> or <? super T>) places a restriction on the type by saying that it either has to extend a specific type (<? extends T> is known as an upper bound), or has to be an ancestor of a specific type (<? super T> is known as a lower bound).
The Java Tutorials have some pretty good explanations of generics in the articles Wildcards and More Fun with Wildcards.
If you have a class hierarchy A, B is a subclass of A, and C and D are both subclasses of B like below
class A {}
class B extends A {}
class C extends B {}
class D extends B {}
Then
List<? extends A> la;
la = new ArrayList<B>();
la = new ArrayList<C>();
la = new ArrayList<D>();
List<? super B> lb;
lb = new ArrayList<A>(); //fine
lb = new ArrayList<C>(); //will not compile
public void someMethod(List<? extends B> lb) {
B b = lb.get(0); // is fine
lb.add(new C()); //will not compile as we do not know the type of the list, only that it is bounded above by B
}
public void otherMethod(List<? super B> lb) {
B b = lb.get(0); // will not compile as we do not know whether the list is of type B, it may be a List<A> and only contain instances of A
lb.add(new B()); // is fine, as we know that it will be a super type of A
}
A bounded wildcard is like ? extends B where B is some type. That is, the type is unknown but a "bound" can be placed on it. In this case, it is bounded by some class, which is a subclass of B.
Josh Bloch also has a good explanation of when to use super and extends in this google io video talk where he mentions the Producer extends Consumer super mnemonic.
From the presentation slides:
Suppose you want to add bulk methods to Stack<E>
void pushAll(Collection<? extends E> src);
– src is an E producer
void popAll(Collection<? super E> dst);
– dst is an E consumer
There may be times when you'll want to restrict the kinds of types that are allowed to be passed to a type parameter. For example, a method that operates on numbers might only want to accept instances of Number or its subclasses. This is what bounded type parameters are for.
Collection<? extends MyObject>
means that it can accept all object who have IS- A relationship with MyObject (i.e. any object which is a type of myObject or we can say any object of any subclass of MyObject) or a object of MyObject class.
For example:
class MyObject {}
class YourObject extends MyObject{}
class OurObject extends MyObject{}
Then,
Collection<? extends MyObject> myObject;
will accept only MyObject or children of MyObject(i.e. any object of type OurObject or YourObject or MyObject, but not any object of superclass of MyObject).
In general,
If a structure contains elements with a type of the form ? extends E, we can get elements out of the structure, but we cannot put
elements into the structure
List<Integer> ints = new ArrayList<Integer>();
ints.add(1);
ints.add(2);
List<? extends Number> nums = ints;
nums.add(3.14); // compile-time error
assert ints.toString().equals("[1, 2, 3.14]");
To put elements into the structure we need another kind of wildcard called Wildcards with super,
List<Object> objs = Arrays.<Object>asList(2, 3.14, "four");
List<Integer> ints = Arrays.asList(5, 6);
Collections.copy(objs, ints);
assert objs.toString().equals("[5, 6, four]");
public static <T> void copy(List<? super T> dst, List<? extends T> src) {
for (int i = 0; i < src.size(); i++) {
dst.set(i, src.get(i));
}
}
Generic wildcards are created to make methods that operate on Collection more reusable.
For example, if a method has a parameter List<A>, we can only give List<A> to this method. It is a waste for this method's funtion under some circumstances:
If this method only reads objects from List<A>, then we should be allowed to give List<A-sub> to this method. (Because A-sub IS a A)
If this method only inserts objects to List<A>, then we should be allowed to give List<A-super> to this method. (Because A IS a A-super)
learn by example:
consider the sort() method in Collections class which use both extends and super:
public static <T extends Comparable<? super T>> void sort(List<T> list){...}
so
why <T extends Comparable<...>>: becuase we need list items (T) to be a subclass of the Comparable interface.
why Comparable<? super T>: becuase we allow the Comparable type
to be a Comparable of any super type of T.
Consider
interface Comparable<T>{
public int compareTo(T o);
}
public static <T extends Comparable<? super T>> void sort(List<T> list){...}
public static <T extends Comparable<T>> void sort2(List<T> list){...}
class A implements Comparable<A>{
#Override
public int compareTo(A o) {
...
}
}
class B extends A {
}
List<A> listA = new ArrayList<>();
List<B> listB = new ArrayList<>();
sort(listA); //ok
sort(listB); //ok
sort2(listA); //ok
sort2(listB); //Error

Categories