Array of specific class to Collection objects? - java

Maybe I am overlooking something very easy and obvious...
I have a method interface which goes like
private void render(Collection<Object> rows);
Now, the objects I need to pass is an array (from an enum):
Module[] mods = Module.values();
widget.render(mods);
Of course this does not work, but why does this not work:
widget.render(Arrays.asList(mods))
It turns my array to a collection of Module, and Module is an object...

Try changing your method signature to:
private void render(Collection<?> rows);
This is is saying your method takes a Collection with any type of element, whereas before it was saying the Collection should specifically have Object as its type parameter.
Using a wildcard like this will place limitations on how you can use the Collection that is passed into the method, especially in regard to modifying it. You might want to show us what your render method is doing if you want more detailed advice.
This post is worth reading with respect to using wildcarded collections in Java: What is PECS (Producer Extends Consumer Super)?

Since a Collection<Object> is not a Collection<Module>. A nice tutorial about generics is available in PDF version, and is a must-read when you work with generics.
This specific case if for example explained on page 4, in the Generics and Subtyping part.

If you casted each Object in the Collection to Module, example:
if(object instanceof Module)
{
Module m = (Module)object;
//Do stuff here with m
}
Then it should work as well. Otherwise the other two answers also work just fine.

The reasons for this are based on how Java implements generics. The best way I have found to explain it is by precisely comparing arrays and generic collections.
An Arrays Example
With arrays you can do this:
Integer[] myInts = {1,2,3,4};
Number[] myNumber = myInts;
But, what would happen if you try to do this?
Number[0] = 3.14; //attempt of heap pollution
This last line would compile just fine, but if you run this code, you could get an ArrayStoreException.
This means that you can fool the compiler, but you cannot fool the runtime type system. And this is so because arrays are what we call reifiable types. This means that at runtime Java knows that this array was actually instantiated as an array of integers which simply happens to be accessed through a reference of type Number[].
So, as you can see, one thing is the real type of the object, an another thing is the type of the reference that you use to access it, right?
The Problem with Java Generics
Now, the problem with Java generic types is that the type information is discarded by the compiler and it is not available at run time. This process is called type erasure. There are good reason for implementing generics like this in Java, but that's a long story, and it has to do with binary compatibility with pre-existing code.
But the important point here is that since, at runtime there is no type information, there is no way to ensure that we are no committing heap pollution.
For instance,
List<Integer> myInts = new ArrayList<Integer>();
myInts.add(1);
myInts.add(2);
List<Number> myNums = myInts;
myNums.add(3.14); //heap polution
If the Java compiler does not stop you from doing this at compile time, the runtime type system cannot stop you either, because there is no way, at runtime, to determine that this list was supposed to be a list of integers only. The Java runtime would let you put whatever you want into this list, when it should only contain integers, because when it was created, it was declared as a list of integers.
As such, the designers of Java made sure that you cannot fool the compiler. If you cannot fool the compiler (as we can do with arrays) you cannot fool the runtime type system either.
As such, we say that generic types are non-reifiable.
Evidently, this would hamper pollymorphism as well pointed out. The solution is to learn to use two powerful features of Java generics known as covariance and contravariance.
Covariance
With covariance you can read items from a structure, but you cannot write anything into it. All these are valid declarations.
List<? extends Number> myNums = new ArrayList<Integer>();
List<? extends Number> myNums = new ArrayList<Float>()
List<? extends Number> myNums = new ArrayList<Double>()
And you can read from myNums:
Number n = myNums.get(0);
Because you can be sure that whatever the actual list contains, it can be upcasted to a Number (after all anything that extends Number is a Number, right?)
However, you are not allowed to put anything into a covariant structure.
myNumst.add(45L);
This would not be allowed, because Java cannot guarantee what is the actual type of the real object. It can be anything that extends Number, but the compiler cannot be sure. So you can read, but not write.
Contravariance
With contravariance you can do the opposite. You can put things into a generic structure, but you cannot read out from it.
List<Object> myObjs = new List<Object();
myObjs.add("Luke");
myObjs.add("Obi-wan");
List<? super Number> myNums = myObjs;
myNums.add(10);
myNums.add(3.14);
In this case, the actual nature of the object is a List of Objects, and through contravariance, you can put Numbers into it, basically because numbers have Object as the common ancestor. As such, all Numbers are objects, and therefore this is valid.
However, you cannot safely read anything from this contravariant structure assuming that you will get a number.
Number myNum = myNums.get(0); //compiler-error
As you can see, if the compiler allowed you to write this line, you would get a ClassCastException at runtime.
Get/Put Principle
As such, use covariance when you only intend to take generic values out of a structure, use contravariance when you only intend to put generic values into a structure and use the exact generic type when you intend to do both.
The best example I have is the following that copies any kind of numbers from one list into another list.
public static void copy(List<? extends Number> source, List<? super Number> destiny) {
for(Number number : source) {
destiny.add(number);
}
}
Thanks to the powers of covariance and contravariance this works for a case like this:
List<Integer> myInts = asList(1,2,3,4);
List<Integer> myDoubles = asList(3.14, 6.28);
List<Object> myObjs = new ArrayList<Object>();
copy(myInts, myObjs);
copy(myDoubles, myObjs);

Related

In Java, can one get away with using "raw unparameterised class"-es instead of using dummy interfaces?

It's been a while since I touched generics in Java, I have this:
Map<List<MyGenericType>, Set<List<MyGenericType>>> x = new HashMap<>();
In truth, MyGenericType requires a generic parameter, since it is defined like this:
public class MyGenericType<X> {}
I declared x with pre-emptive type-erasure of MyGenericType because I didn't want to make an empty marker interface just for the sake of grouping things. Will this, for the sake of having a collection, work in my favor or will I have to make a marker interface to make this possible?
(EDIT)
That is:
public interface SomeCommonInterface {...}
MyGenericType implements SomeCommonInterface ...
Map<List<SomeCommonInterface>, Set<List<SomeCommonInterface>>> x = new HashMap<>();
Recall that Integer as well as Double both extend Number and in turn, Number extends Object.
A List<Number> is in no way usable as a List<Integer> and vice versa. List<Number> x = new ArrayList<Integer> does not compile. This is called 'invariance', and it is 'correct', because if it did compile, you could add doubles to a list of integers which is obviously not right. It tends to throw people off, though, which is why I mention it. If you want covariance or contravariance, you must opt into this: List<? extends Number> list = new ArrayList<Integer>(); does compile. But then list.add(5); won't, because if it did, you would again be able to add doubles to a list of integers and that wouldn't be right.
Once you entirely omit generics on a thing, all type checking on generics is right out the door, and you don't want that. Can you 'get away with it'? Well, uh, the compiler will toss a bunch of warnings in your face and you turn off quite a bit of type checking. If you mean with 'get away with it': "Does it compile"? Yes, it does. If you mean: "Would this code pass any reasonable java coder's code review"? No, it won't.
Simple solution: What's wrong with using MyGenericType<?> here?
NB: Keys in maps should be immutable; list is not. Using it is therefore quite a bad plan; what is that supposed to represent?
This is not really an answer, but as said in the comment here, I don't think that adding wildcards solves anything.
Depending were the use-site of this Map is, you could have some kind of safety. I was in your shoes a couple of times, the only solution I know is to do:
#Getter
static class Box<T> {
private Map<List<MyGenericType<T>>, Set<List<MyGenericType<T>>>> x = new HashMap<>();
}
i.e.: expose the Map via a "wrapper", that has a type variable defined. If callers provide a type when instantiating Box, you will get the needed type safety:
new Box<Integer>().getX().put(...); // you can only put a MyGenericType<Integer> in here now
Otherwise when using raw types or even ?, you could put different type in the key and value.

How to instance ArrayList<String>[] in Java [duplicate]

I'm having trouble figuring out what type parameter is expected at RHS of the following
ArrayList<Pair<ParseNode,ParseNode>>[] nodes = new ArrayList[indexes.length];
Why a copy of <Pair<ParseNode,ParseNode>> is not legitimate?
Arrays of concrete paramaterized types are inherently broken. Remember arrays are covariant and the array type check is a runtime operation. At runtime all the generics have been type erased, so the Array Store check can't tell <Pair<ParseNode, ParseNode>> from <Pair<BigInteger,IOException>>.
The fundamental contract of a generic is "I, the compiler, promise that if you write code that generates no warnings, you will never get a class cast exception at runtime."
Neither can the compiler guarantee to you that it will be able to give you a compile time error if something that is not an ArrayList<Pair<ParseNode,ParseNode>> is put into that array. Nor can the runtime system guarantee you will get an ArrayStoreException (like the Language Specification says it will) if you add the wrong type, rather than a ClassCastException later when you take it back out. (The second part is really why it's actually illegal rather than just a warning, it would result in an array that doesn't obey the language specification.)
So it doesn't let you declare them that way and forces you to acknowledge the 'unsafe' warning. That way it has said "I told you I can't guarantee there will not be any class cast exceptions as a result of using this array, it's on you to make sure you only put the right things in here."
Java not supports generic arrays. Arrays are covariant, generics are not. This means that if class A extends class B then A[] is also B[]. And code
A[] a = new A[10];
B[] b = a;
is legal.
But it is not same for generics. You could not assign Foo<T> to Foo<X> even if T extends X. And so elements of Foo<T>[] can't be guaranteed type safe.
EDIT
Excuse me for just linking out, but I've found Java theory and practice: Generics gotchas article, that explains everything about arrays covariance better than I even would dream.
Don't use an array. Use another ArrayList.
ArrayList<List<Pair<ParseNode,ParseNode>>> listOfLists = new ArrayList<List<Pair<ParseNode,ParseNode>>>();
listOfLists.add(new ArrayList<<Pair<ParseNode,ParseNode>>());

How to create List<List<T>> in java? [duplicate]

From Effective Java by Joshua Bloch,
Arrays differ from generic type in two important ways. First arrays are covariant. Generics are invariant.
Covariant simply means if X is subtype of Y then X[] will also be sub type of Y[]. Arrays are covariant As string is subtype of Object So
String[] is subtype of Object[]
Invariant simply means irrespective of X being subtype of Y or not ,
List<X> will not be subType of List<Y>.
My question is why the decision to make arrays covariant in Java? There are other SO posts such as Why are Arrays invariant, but Lists covariant?, but they seem to be focussed on Scala and I am not able to follow.
Via wikipedia:
Early versions of Java and C# did not include generics (a.k.a. parametric polymorphism).
In such a setting, making arrays invariant rules out useful polymorphic programs.
For example, consider writing a function to shuffle an array, or a function that tests two arrays for equality using the Object.equals method on the elements. The implementation does not depend on the exact type of element stored in the array, so it should be possible to write a single function that works on all types of arrays. It is easy to implement functions of type
boolean equalArrays (Object[] a1, Object[] a2);
void shuffleArray(Object[] a);
However, if array types were treated as invariant, it would only be possible to call these functions on an array of exactly the type Object[]. One could not, for example, shuffle an array of strings.
Therefore, both Java and C# treat array types covariantly. For instance, in C# string[] is a subtype of object[], and in Java String[] is a subtype of Object[].
This answers the question "Why are arrays covariant?", or more accurately, "Why were arrays made covariant at the time?"
When generics were introduced, they were purposefully not made covariant for reasons pointed out in this answer by Jon Skeet:
No, a List<Dog> is not a List<Animal>. Consider what you can do with a List<Animal> - you can add any animal to it... including a cat. Now, can you logically add a cat to a litter of puppies? Absolutely not.
// Illegal code - because otherwise life would be Bad
List<Dog> dogs = new List<Dog>();
List<Animal> animals = dogs; // Awooga awooga
animals.add(new Cat());
Dog dog = dogs.get(0); // This should be safe, right?
Suddenly you have a very confused cat.
The original motivation for making arrays covariant described in the wikipedia article didn't apply to generics because wildcards made the expression of covariance (and contravariance) possible, for example:
boolean equalLists(List<?> l1, List<?> l2);
void shuffleList(List<?> l);
The reason is that every array knows its element type during runtime, while generic collection doesn't because of type erasure.
For example:
String[] strings = new String[2];
Object[] objects = strings; // valid, String[] is Object[]
objects[0] = 12; // error, would cause java.lang.ArrayStoreException: java.lang.Integer during runtime
If this was allowed with generic collections:
List<String> strings = new ArrayList<String>();
List<Object> objects = strings; // let's say it is valid
objects.add(12); // invalid, Integer should not be put into List<String> but there is no information during runtime to catch this
But this would cause problems later when someone would try to access the list:
String first = strings.get(0); // would cause ClassCastException, trying to assign 12 to String
May be this help:-
Generics are not covariant
Arrays in the Java language are covariant -- which means that if Integer extends Number (which it does), then not only is an Integer also a Number, but an Integer[] is also a Number[], and you are free to pass or assign an Integer[] where a Number[] is called for. (More formally, if Number is a supertype of Integer, then Number[] is a supertype of Integer[].) You might think the same is true of generic types as well -- that List<Number> is a supertype of List<Integer>, and that you can pass a List<Integer> where a List<Number> is expected. Unfortunately, it doesn't work that way.
It turns out there's a good reason it doesn't work that way: It would break the type safety generics were supposed to provide. Imagine you could assign a List<Integer> to a List<Number>.
Then the following code would allow you to put something that wasn't an Integer into a List<Integer>:
List<Integer> li = new ArrayList<Integer>();
List<Number> ln = li; // illegal
ln.add(new Float(3.1415));
Because ln is a List<Number>, adding a Float to it seems perfectly legal. But if ln were aliased with li, then it would break the type-safety promise implicit in the definition of li -- that it is a list of integers, which is why generic types cannot be covariant.
An important feature of parametric types is the ability to write polymorphic algorithms, i.e. algorithms that operate on a data structure regardless of its parameter value, such as Arrays.sort().
With generics, that's done with wildcard types:
<E extends Comparable<E>> void sort(E[]);
To be truly useful, wildcard types require wildcard capture, and that requires the notion of a type parameter. None of that was available at the time arrays were added to Java, and makings arrays of reference type covariant permitted a far simpler way to permit polymorphic algorithms:
void sort(Comparable[]);
However, that simplicity opened a loophole in the static type system:
String[] strings = {"hello"};
Object[] objects = strings;
objects[0] = 1; // throws ArrayStoreException
requiring a runtime check of every write access to an array of reference type.
In a nutshell, the newer approach embodied by generics makes the type system more complex, but also more statically type safe, while the older approach was simpler, and less statically type safe. The designers of the language opted for the simpler approach, having more important things to do than closing a small loophole in the type system that rarely causes problems. Later, when Java was established, and the pressing needs taken care of, they had the resources to do it right for generics (but changing it for arrays would have broken existing Java programs).
Arrays are covariant for at least two reasons:
It is useful for collections that hold information which will never change to be covariant. For a collection of T to be covariant, its backing store must also be covariant. While one could design an immutable T collection which did not use a T[] as its backing store (e.g. using a tree or linked list), such a collection would be unlikely to perform as well as one backed by an array. One might argue that a better way to provide for covariant immutable collections would have been to define a "covariant immutable array" type they could use a backing store, but simply allowing array covariance was probably easier.
Arrays will frequently be mutated by code which doesn't know what type of thing is going to be in them, but won't put into the array anything which wasn't read out of that same array. A prime example of this is sorting code. Conceptually it might have been possible for array types to include methods to swap or permute elements (such methods could be equally applicable to any array type), or define an "array manipulator" object which hold a reference to an array and one or more things that had been read from it, and could include methods to store previously-read items into the array from which they had come. If arrays were not covariant, user code would not be able to define such a type, but the runtime could have included some specialized methods.
The fact that arrays are covariant may be viewed as an ugly hack, but in most cases it facilitates the creation of working code.
I think they made a wrong decision at the first place that made array covariant. It breaks the type safety as it described here and they got stuck with that because of backward compatibility and after that they tried to not make the same mistake for generic.
And that's one of the reasons that Joshua Bloch prefers lists to arra ys in Item 25 of book "Effective Java(second edition)"
Generics are invariant: from JSL 4.10:
...Subtyping does not extend through generic types: T <: U does not
imply that C<T> <: C<U> ...
and a few lines further, JLS also explains that Arrays are covariant (first bullet):
4.10.3 Subtyping among Array Types
My take: When code is expecting an array A[] and you give it B[] where B is a subclass of A, there's only two things to worry about: what happens when you read an array element, and what happens if you write it. So it's not hard to write language rules to ensure that type safety is preserved in all cases (the main rule being that an ArrayStoreException could be thrown if you try to stick an A into a B[]). For a generic, though, when you declare a class SomeClass<T>, there can be any number of ways T is used in the body of the class, and I'm guessing it's just way too complicated to work out all the possible combinations to write rules about when things are allowed and when they aren't.

Basic java question : Type casting [duplicate]

This question already has answers here:
Closed 13 years ago.
Possible Duplicate:
How to cast generic List types in java?
When this is feasible
Number number = new Integer("");
Why not this ?
List<Number> list = new LinkedList<Integer>();
This could help :
http://www.ibm.com/developerworks/java/library/j-jtp01255.html
Basically, since you declared a list of Number, the API would allow you to enter any kind of Number in it, including a Number that is not an Integer.
You're almost there. Type parameters in Java are neither implicitly covariant nor contravariant; that is, they're invariant. You can use bounded wildcards to write almost the same declaration:
final List<? extends Number> number = new LinkedList<Integer>();
Given that your list starts out empty here, and you intend to insert items, you're looking for covariance: namely, that you can insert any item that's of type Number, such as Integer. Note that the LinkedList constructor is intending to lock things down to allow insertions only of type Integer, and the bound reference number no longer knows exactly what type of Number that actual list can accommodate. You get covariant reading from number, as you'd expect:
final Number n = number.get(0);
But you can't insert a Number because the the reference's type can't guarantee proper covariance between the element to be inserted and the declared type of the list itself.
number.add(new Integer(0));
That expression doesn't compile; an Integer is "some kind of Number," but it's not guaranteed to match "a specific kind of Number," which could be, say, Double.
I always find it useful to give an example of why this isn't allowed. Consider the following code:
// Create and initialise a list of integers
List<Integer> myIntegerList = new ArrayList<Integer>();
myIntegerList.add(1);
// Some other stuff, possibly passing the list through method calls
List<Number> numbers = myIntegerList;
numbers.add(2.0d); // Legal, since this is a list of double
// And at some point later...
myIntegerList.get(1); // throws ClassCastException because element is a Double
Basically, if generics were covariant, then they wouldn't be worth anything as you would have no guarantee that the generics would limit the list to the supposed contents (which is their whole raison d'etre, after all). The way that you expected things to work, any list could be passed into a List<Object> variable and then any objects at all could be legally put inside it.
As shown above, you can use wildcards to convey the fact that it can be some kind of list of numbers; but then you cannot add elements to the list because it cannot be guaranteed that the elements are of the correct type.
Consider the fact that this works, might give you an idea of what's going on:
List<? extends Number> list = new LinkedList<Integer>();
The extends indicates the relation between the generic types on each side of the equation.
There is a difference between C++ template and Java generics. Java's generics are a compiler time type checking. In case of C++, the C++ compiler compiles a whole new class depending upon the template. IN case of Java, it is only Compile time type checking and this information is removed on the generated byte code. Hence generics are not covariant (they do not know who is the parent and who is the child).
This question goes beyond any specifics of Java Generics. It's more about subtyping in general.
Number has only read methods, while List has write methods too. Suppose for a minute that Number has write methods too
class Number
void set(Number that){...}
Integer i = 1;
Float f = 2.5;
Number n = i;
n.set(f); // same as i.set(f), that's trouble
In this experiment, Integer should not be a subtype of Number anymore, because it cannot support a method in Number.
Another example, Square and Rectangle. Which one should be the super type, which one should be the sub type? It really depends on what methods they have.
Coming from our mathematical training we "intuitively" think of those objects as immutable. Even List feels like an immutable sequence of objects. "Subtype" is often equated to "sub set" in mathematics.
In imperative programming that's not the case, and that creates a lot of mental conflicts.
List<? extends Number> list = new LinkedList<Integer>();

Java Generics: List, List<Object>, List<?>

Can someone explained, as detailed as possible, the differences between the following types?
List
List<Object>
List<?>
Let me make this more specific. When would I want to use
// 1
public void CanYouGiveMeAnAnswer(List l) { }
// 2
public void CanYouGiveMeAnAnswer(List<Object> l) { }
// 3
public void CanYouGiveMeAnAnswer(List<?> l) { }
As the other posts have noted, you are asking about a Java feature called generics. In C++, this is called templates. This feature in Java is usually easier to work with than the that found in C++.
Let me answer your questions functionally (if that's not a naughty word for OO discussions).
Before generics, there were concrete classes like Vector.
Vector V = new Vector();
Vectors hold any object you give them.
V.add("This is an element");
V.add(new Integer(2));
v.add(new Hashtable());
They do this by casting all values given to it into an Object (the root of all Java classes). When you attempt to retrieve the values stored in your Vector, you need to cast the value back into the original class (if you want to do anything meaningful with it).
String s = (String) v.get(0);
Integer i = (Integer) v.get(1);
Hashtable h = (Hashtable) v.get(2);
Casting gets old fast. More than that, the compiler complains to you about unchecked casts. The most urgent problem with casting like this is that consumers of your Vector have to know the classes of its values at compile time in order to cast correctly. In cases where the producer of the Vector and the consumer of the same are completely isolated from each other (think RPC messages), this can be a fatal issue.
Enter generics. Generics attempt to create strongly typed classes to do generic operations.
ArrayList<String> aList = new ArrayList<String>();
aList.add("One");
String element = aList.get(0); // no cast needed
System.out.println("Got one: " + element);
The Design Patterns book encourages the reader to think in terms of contracts, not concrete types. There is wisdom (and code re-use) in divorcing variables from their implementing class.
With this in mind, you might think that all implementations List objects should do the same set of things: add(), get(), size(), etc. With a little reflection, you can imagine many implementations of List operations that obey the List contract in various ways (e.g. ArrayList). However, the type of data these objects deal with is orthogonal to the actions performed on them.
Put it all together and you'll see the following kinds of code frequently:
List<String> L = new ArrayList<String>();
You should read that as "L is a kind of List that deals with String objects". When you start dealing with Factory classes, it is critical to deal with contracts rather than specific implementations. Factories produce objects of various types at runtime.
Using generics is pretty easy (most of the time).
One day you may decide you want to implement your own generic class. Perhaps you want to write a new database abstraction interface that elides the differencesbetween various data stores. When you define that generic class, you will use <t> as a placeholder for the kind of object that will be manipulated by the methods.
If you are still confused, use the generic classes for List until you are comfortable. Later, you can dive into the implementation with a bit more confidence. Or you can look at the source code for the various List classes that ship with the JRE. Open source is great that way.
Have a look at the Oracle/Sun docs about generics.
Cheers.
In my own simple terms:
List
Would declare an ordinary collection, can hold any type, and will always return Object.
List<Object>
Will create a list that can hold any type of object, but can only get assigned a another List<Object>
For instance this doesn't work;
List<Object> l = new ArrayList<String>();
Of course you can add anything but only can pull Object.
List<Object> l = new ArrayList<Object>();
l.add( new Employee() );
l.add( new String() );
Object o = l.get( 0 );
Object o2 = l.get( 1 );
Finally
List<?>
Will let you assign any type, including
List <?> l = new ArrayList();
List <?> l2 = new ArrayList<String>();
This would be called collection of unknown and since the common denominator of unknown is Object you will be able to fetch Objects ( a coincidence )
The importance of unknown comes when its used with subclassing:
List<? extends Collection> l = new ArrayList<TreeSet>(); // compiles
List<? extends Collection> l = new ArrayList<String>(); // doesn't,
// because String is not part of *Collection* inheritance tree.
I hope using Collection as the type doesn't create confusion, that was the only tree that came to my mind.
The difference here, is that l is a collection of unknow that belongs to the Collection hierarchy.
To add to the already good answers here:
Method arguments:
List<? extends Foo>
good choice if you don't intend to alter the list, and only care that everything in the list is assignable to type 'Foo'. This way, the caller can pass in a List<FooSubclass> and your method works. Usually the best choice.
List<Foo>
good choice if you intend to add Foo objects to the list in your method. The caller may not pass in a List<FooSubclass>, as you intend to add a Foo to the List.
List<? super Foo>
good choice if you intend to add Foo objects to the list, and it's not important what else is in the list (ie, you are ok getting a List<Object> that contains a 'Dog' that has nothing to do with Foo).
Method return values
just like method arguments, but with the benefits reversed.
List<? extends Foo>
Guarantees that everything in the returned List has type 'Foo'. It might be List<FooSubclass> though. Caller cannot add to the List. This is your go-to choice and the most common case by far.
List<Foo>
Just like List<? extends Foo> but also allows the caller to add to the List. Less common.
List<? super Foo>
allows the caller to add Foo objects to the List, but does not guarantee what will be returned from list.get(0)... it could be anything from Foo to Object. The only guarantee is that this won't be a list of 'Dog' or some other choice that would prevent list.add(foo) from being legal. Very rare use case.
I hope that helps. Good luck!
ps. To sum up... two questions...
do you need to add to the List? Do you care what is in the list?
yes yes - use List<Foo>.
yes no - use List<? super Foo>.
no yes - use <? extends Foo> --- most common.
no no - use <?>.
I refer you to the excellent Java Generics tutorial, and the "advanced" Generics tutorial, both available from Sun Microsystems. Another great resource is the Java Generics and Collections book.
I will try to answer this in detail. Before generics we were having only List (a raw list) and it can hold almost anything we can think of.
List rawList = new ArrayList();
rawList.add("String Item");
rawList.add(new Car("VW"));
rawList.add(new Runnable() {
#Override
public void run() {
// do some work.
}
});
The major problem with the raw list is when we want to get any element out of such list it can only guarantee that it would be Object and for that reason we need to use casting as:
Object item = rawList.get(0); // we get object without casting.
String sameItem = (String) rawList.get(0); // we can use casting which may fail at runtime.
So conclusion is a List can store Object (almost everything is Object in Java) and always returns an Object.
Generics
Now lets talk about generics. Consider the following example:
List<String> stringsList = new ArrayList<>();
stringsList.add("Apple");
stringsList.add("Ball");
stringsList.add(new Car("Fiat")); //error
String stringItem = stringsList.get(0);
In the above case we cannot insert anything other than String in stringsList as Java compiler applies strong type checking to generic code and issues errors if the code violates type safety. And we get error when we try to insert a Car instance in it. Also it eliminates cast as you can check when we invoke get method. Check this link for understanding why we should use generics.
List<Object>
If you read about type erasure then you will understand that List<String>, List<Long>, List<Animal> etc. will be having different static types at compile time but will have same dynamic type List at run time.
If we have List<Object> then it can store only Object in it and almost everything is Object in Java. So we can have:
List<Object> objectList = new ArrayList<Object>();
objectList.add("String Item");
objectList.add(new Car("VW"));
objectList.add(new Runnable() {
#Override
public void run() {
}
});
Object item = objectList.get(0); // we get object without casting as list contains Object
String sameItem = (String) objectList.get(0); // we can use casting which may fail at runtime.
It seems List<Object> and List are same but actually they are not. Consider the following case:
List<String> tempStringList = new ArrayList<>();
rawList = tempStringList; // Ok as we can assign any list to raw list.
objectList = tempStringList; // error as List<String> is not subtype of List<Obejct> becuase generics are not convariant.
You can see we can assign any list to raw list and major reason for that is to allow backward compatibility. Also List<String> will be converted to List at run time due to type erasure and assignment will be fine anyways.
But List<Object> means it can only refer to a list of objects and can also store objects only. Even though String is subtype of Object we cannot assign List<String> to List<Object> as generics are not covariant like arrays. They are invariant. Also check this link for more. Also check the difference between List and List<Object> in this question.
List<?>
Now we are left with List<?> which basically means list of unknown type and can refer to any list.
List<?> crazyList = new ArrayList<String>();
List<String> stringsList = new ArrayList<>();
stringsList.add("Apple");
stringsList.add("Ball");
crazyList = stringsList; // fine
The character ? is known as wildcard and List<?> is a list of unbounded wildcard. There are certain points to observe now.
We cannot instantiate this list as the following code will not compile:
List<?> crazyList = new ArrayList<?>(); // any list.
We can say a wildcard parameterized type is more like an interface type as we can use it to refer to an object of compatible type but not for itself.
List<?> crazyList2 = new ArrayList<String>();
We cannot insert any item to it as we don't know what actually the type would be.
crazyList2.add("Apple"); // error as you dont actually know what is that type.
Now question arises When would I want to use List<?>?
You can think of this as a read-only list where you don't care about the type of the items. You can use it to invoke methods like returning the length of the list, printing it etc.
public static void print(List<?> list){
System.out.println(list);
}
You can also check the difference between List, List<?>, List<T>, List<E>, and List<Object> here.
Simplest explanation which is not "RTFM":
List
Will generate lots of compiler warnings, but is mostly equivalent to:
List<Object>
While:
List<?>
basically means its something generic, but you don't know what the generic type is. Its great for getting rid of compiler warnings when you cant modify the return types of other things that just returned List. Its much more useful in the form:
List<? extends SomeOtherThing>
The shortest possible explanation is: The second item is a list that can hold any type, and you can add objects to it:
List<Object>
The first item you list is treated as essentially equivalent to this, except you will get compiler warnings because it is a "raw type".
List
The third is a list that can hold any type, but you cannot add anything to it:
List<?>
Basically, you use the second form (List<Object>) when you truly have a list that can contain any object and you want to be able to add elements to the list. You use the third form (List<?>)when you receive the list as a method return value and you will iterate over the list but never add anything to it Never use the first form (List) in new code compiling under Java 5 or later.
I'd put it this way: While List and List<Object> can contain any type of objects, List<?> contains elements of an unknown type, but once that type is captured, it can only contain elements of that type. Which is why it is the only type safe variant of those three, and therefore generally preferable.
To complement the tutorials mentioned by Rob, here's a wikibook explaining the topic:
http://en.wikibooks.org/wiki/Java_Programming/Generics
Edit:
No restrictions on type of items in list
Items in list must extend Object
Wildcard used by itself, so it matches anything
Would it be naive of me to conclude at this point that there's hardly any/no difference at all?
When would I want to use
public void CanYouGiveMeAnAnswer( List l ){}
When you cant to do all the casting your self.
When would I want to use
public void CanYouGiveMeAnAnswer( List l<Object> ){}
When you want to restrict the type of the List. For instance, this would be an invalid argument.
new ArrayList<String>();
When would I want to use
public void CanYouGiveMeAnAnswer( List l<?> ){}
Mostly never.
List, List<?>, and List<? extends Object> are the same thing. The second is more explicit. For a list of this type, you cannot know what types are legal to put into it, and you don't know anything about the types you can get out of it, except that they will be objects.
List<Object> specifically means that the list contains any sort of object.
Let's say we make a list of Foo:
List<Foo> foos= new ArrayList<Foo>();
It is not legal to put a Bar into foos.
foos.add(new Bar()); // NOT OK!
It is always legal to put anything into a List<Object>.
List<Object> objs = new ArrayList<Object>();
objs.add(new Foo());
objs.add(new Bar());
But you mustn't be allowed to put a Bar into a List<Foo> - that's the whole point. So that means that this:
List<Object> objs = foos; // NOT OK!
is not legal.
But it's ok to say that foos is a list of something but we don't know specifically what it is:
List<?> dontKnows = foos;
But that then means that it must be prohibited to go
dontKnows.add(new Foo()); // NOT OK
dontKnows.add(new Bar()); // NOT OK
because the variable dontKnows does't know what types are legal.
List < Object > is meant to pass input type parameter of an Object. While List < ? > represents Wildcard type. The wildcard < ? > is of Unknown parameter type. The wildcard cannot be used as a type argument for a generic method and cannot be used to create a generic instance of a class. Wildcard can be used to extend a subtype class, List < ? extends Number >. To relax the restriction of an Object type and in this case to relax "Number" Object type.
In terms of List functionality differences, the other responses already answered this question. In terms of applicable rules of Java generics, it's a complicated topic. I wrote an in-depth article about Java generics rules, this is the link: https://medium.com/#royalilin/java-generics-rules-1d05de86e9cb

Categories

Resources