How to assign List<? extends BaseClass> to List<BaseClass> - java

having
class BaseClass implements IData ();
class ChildClassA() extends BaseClass;
class ChildClassB() extends BaseClass;
since cannot do
List<BaseClass> aList = new ArrayList<ChildClassA>()
so there is a
List<? extends IData> aList
for pointint to either
ArrayList<ChildClassA>(),
or
ArrayList<ChildClassB>()
the aList is built by other routing at runtime, and that part of code has a function to take a List<IData> from the the aList
the question is if the List<? extends IData> aList is point to ArrayList<ChildClassA>() or ArrayList<ChildClassB>(),
can it do ListData<IData> outputList = (List<IData>) aList? something like below:
(seems it is working, but not sure if there is better way to assign the generics array other than casting.)
Edit: the output of the List<IData> outputList is for read only use (immutable), no insert/delete on it, it will only iterate the IData to react on what the IData really is.
List<? extends IData> aList = new ArrayList<ChildClassA>();
ListData<IData> outputList = (List<IData>)aList
List<? extends IData> aList = new ArrayList<ChildClassB>();
ListData<IData> outputList = (List<IData>)aList

tl;dr Use Collections#unmodifiableList:
List<IData> outputList = Collections.unmodifiableList(aList);
For more information on this topic, you might want to get familiar with the PECS principle.
It's not possible, because the two types are incompatible.
A List<BaseClass> is just what it is declared, a list of BaseClass objects. More precisely, it makes two guarantees:
objects retrieved from it are assignable to BaseClass
every object that is assignable to BaseClass can be added to it (and no other)
A List<? extends BaseClass> is a more loose declaration. Precisely, it simply does not make the second guarantee. However, not only the guarantee is gone, but it is now impossible to add items to it, since the exact generic type of the list is undefined. It might even change for the same list declaration (not the same list object) at runtime.
As a consequence, a List<? extends BaseClass> is not assignable to a List<BaseClass>, since the latter makes a guarantee the first is unable to fulfill.
Practically speaking, consider the following method:
public List<BaseClass> makeList() {
// TODO implement method
return null;
}
If someone implements this method, returning a List<? extends BaseClass>, a client using this method would be unable to add items to it, although its declaration indicates otherwise.
Because of that, such an assignment results in a compilation error.
To fix the example problem the loose declaration can be added to the method:
public List<? extends BaseClass> makeList() {
// TODO implement method
return null;
}
This will signal every client, that the list returned from this method is not meant for adding items to it.
Now let's get back to your use case. In my opinion the most appropriate fix is to the rephrase the function that
take[s] a List from the the aList.
As it seems it is currently declared as
public void useList(List<BaseClass> list);
but since it does not add items to the list, it should be declared as
public void useList(List<? extends BaseClass> list);
However, if that method is part of a currently unchangeable API, you can still do:
List<? extends BaseClass> list;
....
List<BaseClass> tmp = Collections.unmodifiableList(list);
useList(tmp);

No, it is unsafe.
After that cast it would be legit to add to the list that is supposed to contain only ChildClassA typed elements, element of the other child type ChildClassB type and vice-versa.
We can simply your code a bit to make it more obvious why this should not be allowed:
List<ChildClassA> aList = new ArrayList<ChildClassA>();
aList.add(a1);
aList.add(a2);
//...
List<IData> iDataList = (List<IData>) aList;
iDataList.add(b1);
iDataList.add(b2);
//...
for (ChildClassA a : aList) {
// a some point a is going to be assigned b1 or b2 and they results in cast
// exception.
}
Note that iDataList makes reference to the very same list object as aList.
If that cast was allowed then you would be able to add elements to aList that are not ChildClassA instances.
The best solution is on the details.
If the problem is that a third-party library requires a List<IData> typed reference and as long as it is only for reading you can use a unmodifiable proxy as returned by Collections.unmodifiableList:
import java.util.Collections;
//...
final List<ChildClassA> aList = new ArrayList<>();
//... add stuff to aList
final List<IData> readOnlyIDataList = Collections.unmodifiableList(aList);
//... read only access operations readOnlyIDataList

Related

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.

Generic Collection

This is part of the Java (1.6) Collection interface:
public interface Collection<E> extends java.lang.Iterable<E> {
/* ... */
boolean containsAll(java.util.Collection<?> objects);
boolean addAll(java.util.Collection<? extends E> es);
boolean removeAll(java.util.Collection<?> objects);
boolean retainAll(java.util.Collection<?> objects);
/* ... */
}
Why does addAll have <? extends E> while removeAll has <?>
I did not know, I googled. I got this explaination here: http://www.ibm.com/developerworks/java/library/j-jtp01255/index.html
Copying the part:
One element of the generifed Collections API that is often confusing at first is the signatures of containsAll(), removeAll(), and retainAll(). You might expect the signatures for remove() and removeAll() to be:
interface Collection<E> {
public boolean remove(E e); // not really
public void removeAll(Collection<? extends E> c); // not really
}
But it is in fact:
interface Collection<E> {
public boolean remove(Object o);
public void removeAll(Collection<?> c);
}
Why is this? Again, the answer lies in backward compatibility. The interface contract of x.remove(o) means "if o is contained in x, remove it; otherwise, do nothing." If x is a generic collection, o does not have to be type-compatible with the type parameter of x. If removeAll() were generified to only be callable if its argument was type-compatible (Collection<? extends E>), then certain sequences of code that were legal before generics would become illegal, like this one:
// a collection of Integers
Collection c = new HashSet();
// a collection of Objects
Collection r = new HashSet();
c.removeAll(r);
If the above fragment were generified in the obvious way (making c a Collection<Integer> and r a Collection<Object>), then the code above would not compile if the signature of removeAll() required its argument to be a Collection<? extends E>, instead of being a no-op. One of the key goals of generifying the class libraries was to not break or change the semantics of existing code, so remove(), removeAll(), retainAll(), and containsAll() had to be defined with a weaker type constraint than they might have had they been redesigned from scratch for generics.
For any collection containing elements of type E, addAll must be able to deal with input collections not just of E, but all of its subclasses as well. Hence <? extends E>. Without this, you could not add all elements of a List<Integer> to a List<Number>, which would clearly not be right.*
For removal, the limits need not be so strictly set, and there is no harm in trying to remove elements of a collection of some totally unrelated type. E.g. you can have a collection of Numbers, about which you happen to know that it only contains Integers, so passing it to removeAll on a List<Integer> should work fine, and it would be stupid for the compiler to disallow this.
Note that according to the Javadoc, removeAll may optionally throw a ClassCastException, depending on implementation.
*The reason behind this is that in Java, generics are invariant. For more details, see e.g. this thread.
<?> is less restrictive than <? extends E>.
There is nothing wrong with removing an orange from a collection of apples; there are a lot of things wrong with adding an orange to a collection of apples.
When you add item to your collection you want to be sure that they do have a certain type.
When you remove them, only those in the collection are removed. Regardless of their type.
Java implements generics through erasure. These info are only for compilation time only. I guess the java collection designers did this to retain more ascendent compatibility with pre-generics java version.
when you add an object, it needs to be a subclass (or sub-subclass, etc.) of the main type. When you remove an object, it returns it as the type oc the collection. This is a good example of polymorphism in action.
A simple example to illustrate what has been said:
public class Test {
public static void main(String[] args) {
List<String> l = new ArrayList<String>();
System.out.println(l.remove(new Object())); //false
System.out.println(l.contains(new Object())); //false
// l.add(new Object()); // does not compile
}
}
Who cares what you try to remove ?
Adding is something else; we wouldn't want to end up with something strange in our collection.
as requested; an example:
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class Main {
private static class A {
}
public static void main(String[] args) {
Collection<A> collection_A = new ArrayList<A>();
Collection<String> collection = new ArrayList<String>();
// no problem to try and remove things that wouldn't be there in the first place; either way, they are gone afterwards
collection.removeAll(collection_A);
// we can't allow this; you would end up with things in your collection that don't belong there
collection.addAll(collection_A);
}
}
To remove restriction is not needed, so only <?>, but while adding we have to check and then add for type safety, so addAll is with restriction <? extends E>
With addAll you want to be able to add all elements that are a subtype of the generic type. This includes adding all elements of a List<String> to a List<Object>. We use ? extends E to accept any Collection that contains the type stored in this collection or any subtype.
boolean addAll(java.util.Collection<? extends E> es);
List<Number> numbers = ...;
List<Integer> integers = ...;
numbers.addAll(integers);//works
boolean addAll(java.util.Collection<E> es);
numbers.addAll(integers);//does not work E != Integer
we can't use ? as that would remove any security provided by generics.
boolean addAll(java.util.Collection<? extends E> es);
List<Number> numbers = ...;
List<Integer> integers = ...;
List<String> strings = ...;
numbers.addAll(integers);//works
numbers.addAll(strings);//error
boolean addAll(java.util.Collection<?> es);
numbers.addAll(strings);//works - now we have strings in our Number collection
We can use ? to remove objects since trying to remove a String from List of Numbers wont affect a List<Number>.
boolean removeAll(java.util.Collection<?> objects);
List<Objects> objects = ...;
List<Integer> integers = ...;
List<Number> numbers = ...;
numbers.removeAll(objects);//works
numbers.removeAll(integers);//works
boolean removeAll(java.util.Collection<? extends E> objects);
numbers.removeAll(objects);//does not work
numbers.removeAll(integers);//works
boolean removeAll(java.util.Collection<? super E> objects);
numbers.removeAll(objects);//works
numbers.removeAll(integers);//does not work

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 */

Specific generic type of lists within a list

I have a list of lists.
I would like to know how I can restrict the generic types of each of the inner lists so each element of the outer list contains an inner list that can only contain one type of object. So far I have tried this:
List<ArrayList<?>> l = new ArrayList<ArrayList<?>>();
But there are ways to add types of objects to the inner lists which do not belong. Is there a way to specify the type the inner list accepts?
For example, if I have the following inner lists,
ArrayList<T1> innerList = new ArrayList<T1>();
ArrayList<T2> innerList2 = new ArrayList<T2>();
ArrayList<T3> innerList3 = new ArrayList<T3>();
How would I create an outer list which can contain all of the inner lists while retaining the specific type that the inner list contains.
Also I am not sure if this is possible, or if what I am doing is bad design. If it is bad design, insight onto a better design (maybe there is a different collection that does this better) would be very appreciated.
If the types of the inner lists have nothing in common, there is no way to narrow it down, and the wildcard ? is the best you can do.
If T1 T2 and T3 all extend from a base class B, then you can write:
List<List<? extends B>> outerList = new ArrayList<List<? extends B>>();
Or likewise if they share an interface. It depends on what common functionality they are implementing that requires them to be stored in the same list.
If you want help with the design you will need to explain your situation with an example/use case. It's probably not good design to keep them in the same collection if they have nothing in common.
This is not possible. Generic information is only available at compile time. However, the exact contents and structure of a list of lists will not be known until runtime. Thus, the compiler, cannot make any assurances about what each list will contain. If you do know in advance the structure of the list then it would be better to consider a holding class eg.
class Holder<T,S> {
List<T> listOfTs;
List<S> listOfSs;
}
If you know that the lists will all share a common supertype then you may wish to use wildcard bounding.
List<List<? extends Shape>> list = new ArrayList<List<? extends Shape>>();
list.add(new ArrayList<Circle>());
list.add(new ArrayList<Square>());
This will allow you to manipulate the lists according to their supertype. The problem with wildcard bounding is that you cannot add any elements to wildcard bounded collections.
Consider the following:
List<? extends Shape> list = new ArrayList<Circle>();
list.add(new Square());
// element is a valid shape, but not a valid circle
// contract of the original list is broken.
If you know you are only ever going to use a certain number of generics you could store the class that each represents and use this to cast the lists in a type safe way.
class ListHolder<T> {
private final Class<T> clazz;
private final List<T> list;
public ListHolder(Class<T> clazz) {
this.clazz = clazz;
this.list = new ArrayList<T>();
}
public boolean isCircleList() {
return this.clazz == Circle.class;
}
public List<Circle> getCircleList() {
if (!isCircleList()) {
throw new IllegalStateException("list does not contain circles");
}
return (List<Circle>) list;
}
public boolean isRectangleList() {
return this.clazz == Rectangle.class;
}
public List<Rectangle> getRectangleList() {
if (!isRectangleList()) {
throw new IllegalStateException("list does not contain rectangles");
}
return (List<Rectangle>) list;
}
public static void main(String[] args) {
ListHolder<Rectangle> rectangleListHolder = new ListHolder<Rectangle>(Rectangle.class );
List<ListHolder<? extends Shape>> list = new ArrayList<ListHolder<? extends Shape>>();
list.add(rectangleListHolder);
ListHolder<? extends Shape> shapeWildCardList = list.get(0);
List<Rectangle> rectangles = shapeWildCardList.getRectangleList();
}
}
If you have a fixed number of types which could go into such lists you could create a subtype for each of those types. This would also have the advantage that you could use instanceof to determine the type of your list, which you probably want to do if you have a list of differently typed lists.
// common supertype for the lists
abstract class SpecialList<T> extends LinkedList<T> {}
// a list for every type:
class T1List extends SpecialList<T1> {}
class T2List extends SpecialList<T2> {}
class T3List extends SpecialList<T3> {}
//use
List<SpecialList<?>> l = new ArrayList<SpecialList<?>>();

Transfering a Set with a Wildcarded Generic to a List in Java

I have a data type that contains a set and a method that expects List<? extends MyClass>. The data type has Set<? extends MyClass>. I need to be able to move the stuff out of the set and into the List. The order it goes into the list doesn't matter, it just needs to start keeping track of it so that it can be reordered when displayed. Suffice to say that changing the Set into a List in the data type is out of the question here.
This seems pretty easy at first. Create a new method that takes a Set instead of a List, changes it into a list and then passes it on to the old method that just took a list. The problem comes in changing the set to a list.
public void setData(Set<? extends MyClass> data) {
List<? extends Myclass> newData = ArrayList< /* What goes here? */ >();
for(ConcordaEntityBean o : data) {
newData.add(o);
}
setData(newData);
}
Obviously, I can't instantiate an ArrayList with a wildcard, it chokes. I don't know the type at that point. Is there some way to pull the type out of data and pass it to ArrayList? Can I just instantiate it with MyClass? Is there some other way to do this?
You can't instantiate it with MyClass because newData is not necessarily of type List<MyClass>, and Java does not support covariance (ie, S extends T does not imply that ArrayList<S> is a subtype of ArrayList<T>).
What you can do is this:
List<MyClass> newData = new ArrayList<MyClass>();
Now, everything in the Set (which is guaranteed to be a subtype of MyClass) can be put into newData.
Generally speaking, wildcard types enforce boundary constraints. You know that Set<? extends MyClass> is a Set parameterized with some type, and that type is bounded from above by MyClass. So when you are dealing with the elements from that Set, you can do no better than to treat them as MyClass objects.
Does this work:
List<MyClass> newData = new ArrayList<MyClass>(data);
setData(newData);
?
Or even a one-liner:
setData(new ArrayList<MyClass>(data));
I know my grasp of writing generic methods is shaky, but it seems like this would work:
public <T extends MyClass> void setData(Set<T extends MyClass> data) {
setData(new ArrayList<T>(data));
}

Categories

Resources