I recently had an exam on Java, and there was a wide section about wildcard generics in Java. However, there is very little said about their usage in practice. When should we use them? Let's see a typical usage:
void check(Collection<? extends Animal> list) {
// Do something
}
What the documentation says, that this collection does not allow to add any elements to the list. So basically wildcards can be used for making collections read-only. Is that their only usage? Is there any practical need for that? For the last four years I took part in a lot of programming projects in Java, but I haven't seen any project that would use extensively such a feature as wildcard.
So, from the practical point of view, are there any situations when wildcard generics are unavoidable and necessary?
So, from the practical point of view, are there any situations when
wildcard generics are unavoidable and necessary?
I don't think they are 'unavoidable and necessary' because the Java compiler erases them anyway. However, when using them you get the benefit of a tighter type check during compile-time and you avoid type casting. Who wants to type cast anyway? :)
Guidelines for Wildcard Use
Type Erasure
void check(Collection<? extends Animal> list) {
list.add(new Animal()); // compile time error
list.add(new Dog()); // compile time error. Dog is subclass of Animal class.
}
Java has develop such generics because to disallow the programmar to code whatever they want otherwise if it is allowed then later they will find a mess in run-time environment.
Sometime in programming you will get a scenario where your method check would not wan't to add element in the list but want to read those element.
You can only add null values.
In brief, what you are saying is wrong.
It has nothing to do to "make collections read-only".
We can still add elements to the input list, because Collection did declare a add(E) method.
The wildcard is straight-forward I think: You actually want to constraint the input type, because your logic is only reasonable for certain type.
For your example, your check maybe using some method of Animal
void check(Collection<? extends Animal> list) {
// Do something
for (Animal a : list) {
a.checkAge(); // checkAge() is method of Animal
}
}
Without ? extends Animal, the above code will not work, as the incoming list can be collection of anything (not Animal).
Therefore, we want to make sure the incoming collection to Collection or Collection etc, so that our code actually make sense as we are retrieving elements from the list and treated the element as Animal.
Related
I have seen a lot of cases where in, the ? type is used only to create references. And we are not able to add any Objects other than null. So, is the use of '?', only to create references?
Also, what is the need / use of giving the option to add a null? They could simply have implemented with no add() method at all, right? Any reason for giving the option of adding null into List < ? > ?
List is a generic interface, and the same interface regardless of the generic type. As such, all implementors are required to redefine all methods in it. As far as being able to add null into an "unbounded" list, it's not as much a matter of methods as much as a requisiste of respecting the interface:
the unbound generic wildcard allows a reference to a List (or other generic class) to be passed around to methods without requiring a compile-time knowledge of the specific type it will contain. However, the contract of the generic must still be respected. This poses no big problem for read operations, since whatever is in the List, it will always (also) be a java.lang.Object. On the other hand, insert operations are almost impossible, since lacking informations about the accepted types, no type can be accepted - e.g. a List can only accept strings, but an Object could be anything else - a Number for example, and thus cannot be accepted safely.
Null works safely since (assuming the List accept null values), it will be the only value that's always valid to be put inside the list (a null can be cast to any valid type).
Edit: just since i noticed i didn't actually answer your question: yes, the <?> wildcard (and the extended forms as well <? extends X> and <? super X> are only valid when defining references, and cannot be used in generic instantiation.
Firstly, null is a member of all types. That's part of the language specification that pre-dates generics, so you can' do anything about that.
As for the use of ?, you can't * instantiate* a wildcard-typed generic class, that you can't do this:
List<? extends Number> list = new ArrayList<? extends Number>(); // error
because it does't make sense. The wildcard type is used to tell the compiler "I don't know what the type of the list will be, but it will be bounded by Number". However, when the code executes, a List with a type bounded by Number will be assigned to the variable.
Wildcards are most often seen as parameters:
public static void doSeomthing(List<? extends Number> list) {
// use the list knowing only that the elements are a subclass of Number
// but knowing which exact class they are
}
This is an Android app, but presumably it happens the same in Java. I have a type LevelFactory from which I derive Level1Factory, Level2Factory, etc. I want to have an array of these classes so I can instantiate a level given the array index.
I can just have a Class[] and put them in that and then just cast them to LevelFactory when I need to use them, but I was wondering what the proper thing to do is.
This is obviously an error "Incompatible types":
new Class<LevelFactory>[] {Level1Factory.class,Level2Factory.class};
However, I was surprised to see that this is also an error "Generic array creation":
new Class<? extends LevelFactory>[] {Level1Factory.class,Level2Factory.class};
The following works, but it gives the "Unchecked assignment" warning when assigned to a variable:
new Class[] {Level1Factory.class,Level2Factory.class};
The last is the only option I can get to work. I just ignore the warning, but I would like to do it using generics if that's actually possible.
I would recommend you to read Item 25 "Prefer lists to arrays" of book "Effective Java". There Joshua Bloch writes:
Why is it illegal to create a generic array? Because it isn’t typesafe. If it were
legal, casts generated by the compiler in an otherwise correct program could fail at
runtime with a ClassCastException. This would violate the fundamental guarantee provided by the generic type system.
UPD: Maybe with concrete example it would be more understandable.
First of all arrays are covariant which means that SuperClass[] can be cast to SubClass[] and vice versa. It also means that it's legal to cast AnyConcreteClass[] to, say, Object[].
So Let's assume that it's possible to have Set<Cat>[] (but it is NOT). If somebody cast this array to Object[] and then add there a set of Dog instances, Java couldn't guarantee anymore that our array contains only sets of Cat instances. Breaking type safety it breaks essence of generics. That is why it's illegal have generic array.
Set<Cat>[] cats = new Set<Cat>[]; // illegal
Object[] objects = cats;
objects[1] = new Set<Dog>();
cats[1].add(new Cat()); // Oops! TypeCastException
Honestly saying this example also was taken from Effective Java :)
Two questions:
Do you really need an Array? Arrays don't work great with generics. So an ArrayList<LevelFactory> might be the better solution
Do you really need the downcast to the special type (Level1Factory, Level2Factory)? If they have a common super method which is defined in LevelFactory (lets say Level getLevel()) you should not need to downcast them. Just call getLevel() and you get the correct Level instance from your factory.
Another note because this seems to be a common pitfall:
new Class<? extends LevelFactory>
This is not a valid statement (it does not matter if its an array or not). <? extends T> is only valid for the type on the left side. It defines that the generic type of the created Object can be T or derived from T. For Collections this does not mean that they can store objects of T or derived from T (which can a Collections of T anyway).
List<LevelFactory> list = new ArrayList<LevelFactory>() This means you can add objects of LevelFactory, Level1Factory and Level2Factory to list. When you want to receive objects from list they are of type LevelFactory.
List<? extends LevelFactory> list = new ArrayList<LevelFactory>() Means you can receive objects of LevelFactory from list. BUT you cannot add any object to list in a typesafe way because you don't know the exact generic type of list. That because you can also assign new ArrayList<Level1Factory>() to list. Which means that you can't even add LevelFactory objects to list because they don't implement Level1Factory.
In general <? extends Something> on collections is not what you want in most cases.
You can't create array this way new SomeClass<Type>[10] but only this way new SomeClass[10]. Consider using ArrayList<SomeClass<Type>> instead.
I have read that the following code snippet will result in a compiler error
void tryAddingToList(List<? extends SuperType> list) {
list.add(new SubType());
}
Now my question is:
Who exactly sets the restriction and how is the restriction set? Does the compiler specifically check for the add method call in such a method implementation?
If yes, what if I implement my own List and instead of add, introduce a new addToList method that does the same thing as add, and call that in the tryAddingToList method?
Even better, what if I implement my get method such that it also "secretly" adds something to the list (don't ask me why anyone would do that; I'm just curious).
You should use super instead of extends here.
void tryAddingToList(List<? super SubType> list) {
list.add(new SubType());
}
Assume there are 3 types SuperType, SubType1, SubType2. In your original code, List<? extends SuperType> could be List<SubType2>, and you can't add a SubType1 to List<SubType2>.
You should cast SubType to SuperType explicitly. Because not all objects of SuperType are SubType(may be typed other sub-classes). This may give you some hint. Good luck.
Yes, it's indeed the compiler that applies the restriction. Most of the information (not all) will be erased when actually compiled to byte code. You should interpreted generics as extra information for the developer, so we don't make silly mistakes.
I'm writing a small API to deal with objects that have specific 'traits' In this case, they all have an interval of time and a couple of other bits of data, So I write an interface TimeInterval with some getters and setters.
Now most of these API methods deal with a Set or List of Objects. Internally these methods use the Java Colletions Framework (HashMap/TreeMap in particular). So these API methods are like:
getSomeDataAboutIntervals(List<TimeInterval> intervalObjects);
Couple of Questions:
a) Should this be List<? extends TimeInterval> intervalObjects instead?
Is it mostly a matter of style? The one disadvantage of taking strictly an interface that I can see is, you need to create your list as a List<TimeInterval> rather than List<ObjectThatImplementsTimeInterval>.
This means potentially having to copy a List<Object..> to List<TimeInterval> to pass it to the API.
Are there other pros & cons to either approach?
b) And, one dumb question :) The collections framework guarantees I always get out the same instance I put in, the collections are really a collection of references, correct?
1) Yes.
Method parameters should be as general as possible. List<? extends A> is more general than List<A>, and can be used when you don't need to add things to the list.
If you were only adding to the list (and not reading from it), the most general signature would probably be List<? super A>
Conversely, method return types should be as specific as possible. You rarely to never want to return a wildcard generic from a method.
Sometimes this can lead to generic signatures:
<T extends MyObject> List<T> filterMyObjects(List<T>)
This signature is both as specific and as general as possible
2) Yes, except possibly in some rare very specific cases (I'm thinking of BitSet, although that isn't technically a Collection).
If you declare your list as List<? extends A>, then you can pass in any object which static type is List<X>, where X extends A if A is a class, or X implements A id A is an interface. But you'll not be able to pass in a List or a List<Object> to it (unless A is Object) without force-casting it.
However, if you declare the parameter as a List<A>, you'll only be able to pass lists which static type is strictly equivalent to List<A>, so not List<X> for instance. And by "you are not able to do otherwise", I really mean "unless you force the compiler to shut up and accept it", which I believe one should not do unless dealing with legacy code.
Collections are really collections of references. The abstraction actually is that everything you can put in a variable is a reference to something, unless that variable is of a primitive type.
1) I would recommend ? extends TimeInterval. Because of Java's polymorphism, it may not actually make a difference, but it is more robust and better style
2) Yes
a) No. List<? extends TimeInterval> will only accept interfaces that extend the interface TimeInterval. Your assertion that "you need to create your list as a List<TimeInterval> is wrong, unless I misunderstand your point. Here's an example:
List<List> mylist= new ArrayList<List>();
mylist.add(new ArrayList());
b) Yes.
Should this be List intervalObjects instead?
You only do that if you want to pass in a List<TimeIntervalSubclass>. Note you can put instances of subclasses of TimeInterval into a List<TimeInterval>. Keep in mind that the type of the list is different than the types in the list.
If you do List<? extends A> myList -- that only affects what you can assign to myList, which is different than what is in myList.
And, one dumb question :) The collections framework guarantees I
always get out the same instance I put in, the collections are really
a collection of references, correct?
When you create a collection Map myMap = new HashMap(), myMap is a reference to the underlying collection. Similarly, when you put something into a collection, you are putting the reference to the underlying object into the collection.
I don't understand what is the use of unbound wildcards generics. Bound wildcards generics with upper boundary <? extends Animal> makes perfect sense, because using polymorphism I can work with that type or collection. But what is the point of having generics that can be of any type? Doesn't it defeat the purpose of generics? Compiler doesn't find any conflict and after type erasure it would be like no generics was used.
An unbound type can be useful when your method doesn't really care about the actual type.
A primitive example would be this:
public void printStuff(Iterable<?> stuff) {
for (Object item : stuff) {
System.out.println(item);
}
}
Since PrintStream.println() can handle all reference types (by calling toString()), we don't care what the actual content of that Iterable is.
And the caller can pass in a List<Number> or a Set<String> or a Collection<? extends MySpecificObject<SomeType>>.
Also note that not using generics (which is called using a raw type) at all has a quite different effect: it makes the compiler handle the entire object as if generics don't exist at all. In other words: not just the type parameter of the class is ignored, but also all generic type parameters on methods.
Another important distinctions is that you can't add any (non-null) value to a Collection<?>, but can add all objects to the raw type Collection:
This won't compile, because the type parameter of c is an unknown type (= the wildcard ?), so we can't provide a value that is guaranteed to be assignable to that (except for null, which is assignable to all reference types).
Collection<?> c = new ArrayList<String>();
c.add("foo"); // compilation error
If you leave the type parameter out (i.e. use a raw type), then you can add anything to the collection:
Collection c = new ArrayList<String>();
c.add("foo");
c.add(new Integer(300));
c.add(new Object());
Note that the compiler will warn you not to use a raw type, specifically for this reason: it removes any type checks related to generics.
When you need to perform an instanceof check.
You can't parameterize like this:
Object value;
if (value instanceof List<String>) {
// ...
}
So you do:
Object value;
if (value instanceof List<?>) {
// ...
}
While using raw types means that you don't know about generics (because you're lazy or code was written ages ago), using <?> means that you know about generics and explicitly emphasize that your code can work with any kind of objects.
There are (rare) perfectly correct use cases for unbound wildcards. The SDK contains some of them.
One example is a method that does a definite action on a list of any kind and does not return anything as rotate in Collections:
static void rotate(List<?> list, int distance)
Another example is when you want to list the possible constructors for a class, the method is :
Constructor<?>[] getConstructors()
Here it in not even possible to use a generic, because by definition the array will contain different constructor each with its own actual class. By contrast, the API does use a generic signature for getting one single constructor : Constructor<T> getConstructor(Class<?>... parameterTypes).
The conclusion is that even if it is mainly used for compatibility with older code, there are still places where unbound wildcard generics are the correct way.
Allow me to rephrase the question:
"What is the difference between List<Object> and List<?> ?"
The answer to that is that List<?> is more restrictive. It tells us that we have a bunch of object of some type, but that type is not necessarily Object.
Since we don't know what that type is, we cannot add to the list at all - anything we add may be of wrong type. In fact, we cannot pass any argument of ? type to any method, not just add().
On the plus side, when we specify that a method takes List<?>, it can take List<String> or List<Integer> or any other List<>. List<Object> can only take List<Object>.
Using unbounded wildcards only makes sense, AFAIK, when wrapping old code that is not using generics, basically Collections.
If you look at what you can do with such a generic it's basically nothing.
If you have a collection you can't add anything, if you try to read something out you will always get an Objectand so on.
This in turns helps guaranteeing that you will handle the data in a type safe way, whereas using the raw type would have caused the compiler to ignore any mess you'd make.
Which methods and fields are accessible/inaccessible through a reference variable of a wildcard parameterized type? from Angelika Langers Java Generics FAQ might be of interest.
List<Object> is a List that may contain any Object, e.g. l[0] may be an Integer, l[1] may be a String, etc.
List<?> may be a List<Integer> or List<String>, etc. If it is a List<Integer>, it stores only Integers, if it is List<String>, it stores only Strings.