I am refreshing my knowledge on Java generics. So I turned to the excellent tutorial from Oracle ... and started to put together a presentation for my coworkers. I came across the section on wildcards in the tutorial that says:
Consider the following method, printList:
public static void printList(List<Object> list) {
...
The goal of printList is to print a list of any type, but it fails to achieve that goal — it prints only a list of Object instances; it cannot print List<Integer>, List<String>, List<Double>, and so on, because they are not subtypes of List<Object>. To write a generic printList method, use List<?>:
public static void printList(List<?> list) {
I understand that List<Object> will not work; but I changed the code to
static <E> void printObjects(List<E> list) {
for (E e : list) {
System.out.println(e.toString());
}
}
...
List<Object> objects = Arrays.<Object>asList("1", "two");
printObjects(objects);
List<Integer> integers = Arrays.asList(3, 4);
printObjects(integers);
And guess what; using List<E> I can print different types of Lists without any problem.
Long story short: at least the tutorial indicates that one needs the wildcard to solve this problem; but as shown, it can be solved this way too. So, what am I missing?!
(side note: tested with Java7; so maybe this was a problem with Java5, Java6; but on the other hand, Oracle seems to do a good job regarding updates of their tutorials)
Your approach of using a generic method is strictly more powerful than a version with wildcards, so yes, your approach is possible, too. However, the tutorial does not state that using a wildcard is the only possible solution, so the tutorial is also correct.
What you gain with the wildcard in comparison to the generic method: You have to write less and the interface is "cleaner" since a non generic method is easier to grasp.
Why the generic method is more powerful than the wildcard method: You give the parameter a name which you can reference. For example, consider a method that removes the first element of a list and adds it to the back of the list. With generic parameters, we can do the following:
static <T> boolean rotateOneElement(List<T> l){
return l.add(l.remove(0));
}
with a wildcard, this is not possible since l.remove(0) would return capture-1-of-?, but l.add would require capture-2-of-?. I.e., the compiler is not able to deduce that the result of remove is the same type that add expects. This is contrary to the first example where the compiler can deduce that both is the same type T. This code would not compile:
static boolean rotateOneElement(List<?> l){
return l.add(l.remove(0)); //ERROR!
}
So, what can you do if you want to have a rotateOneElement method with a wildcard, since it is easier to use than the generic solution? The answer is simple: Let the wildcard method call the generic one, then it works:
// Private implementation
private static <T> boolean rotateOneElementImpl(List<T> l){
return l.add(l.remove(0));
}
//Public interface
static void rotateOneElement(List<?> l){
rotateOneElementImpl(l);
}
The standard library uses this trick in a number of places. One of them is, IIRC, Collections.java
Technically, there is no difference between
<E> void printObjects(List<E> list) {
and
void printList(List<?> list) {
When you are declaring a type parameter, and using it only once, it essentially becomes a wildcard parameter.
On the other hand, if you use it more than once, the difference becomes significant. e.g.
<E> void printObjectsExceptOne(List<E> list, E object) {
is completely different than
void printObjects(List<?> list, Object object) {
You might see that first case enforces both types to be same. While there is no restriction in second case.
As a result, if you are going to use a type parameter only once, it does not even make sense to name it. That is why java architects invented so called wildcard arguments (most probably).
Wildcard parameters avoid unnecessary code bloat and make code more readable. If you need two, you have to fall back to regular syntax for type parameters.
Hope this helps.
Both solutions are effectively the same, it's just that in the second one you are naming the wildcard. This can come handy when you want to use the wildcard several times in the signature, but want to make sure that both refer to the same type:
static <E> void printObjects(List<E> list, PrintFormat<E> format) {
Related
Compiling the below code is failing:
public static void swap(List<?> list, int i, int j) {
list.set(i, list.set(j, list.get(i)));
}
like:
Swap.java:5: set(int,capture#282 of ?) in List<capture#282 of ?> cannot be applied to (int,Object)
list.set(i, list.set(j, list.get(i)));
But if I do this:
public static void swap(List<?> list, int i, int j) {
swapHelper(list, i, j);
}
private static <E> void swapHelper(List<E> list, int i, int j) { list.set(i, list.set(j, list.get(i)));
}
Its working perfectly.
But I have a basic doubt here. Generics are said to be invariant, so List<String> is not subtype of List<Object>, right?
If that is the case, then how come in the above method, we are able to pass List<?> to List<E>? How does this works?
the answer is wildcards.
List<?> is not the same as List<Object>
while java assumes both are collections of Object, the former will match the type of any other List<T>, while the latter will not match the type of any List<T> besides List<Object>.
example:
public void doSomething(List<Object> list);
This function will accept only List<Object> as it's parameter. However this:
public void doSomething(List<?> list);
will accept any List<T> as it's parameter.
This can be extremely useful when used with generic constraints. For instance if you'd like to write a function that manipulates numbers (Integers, Floats, etc.) you could:
public void doSomethingToNumbers(List<? extends Number> numbers) { ... }
Because you are using wildcard capturing.
In some cases, the compiler infers the type of a wildcard. For
example, a list may be defined as List but, when evaluating an
expression, the compiler infers a particular type from the code. This
scenario is known as wildcard capture.
Thanks to the helper method, the compiler uses inference to determine T, the capture variable, in the invocation.
List<?> means "list of an unknown type". You can pass a List<String> for it, not because it is a subclass (it isn't), but because "unknown type" is supposed to accept whatever you throw at it.
Because type is unknown, you cannot perform operations with it, that require knowing the type of the elements (like set or add etc).
List<E> is a generic. It is not the same as List<?> even though it kinda looks similar.
One difference is, as you noted, you can do things like set or add to it, because the type of the element is now known.
Whildcards as function parameters are not very useful (<E> void foo(List<E> l) is not much different from void foo(List<?> l)). They also bypass all the compile-time type check, thus defeating the purpose of generics, and should really be avoided.
A more common (and less harmful) use for wildcards is in the return types: List<? extends DataIterface> getData() means that getData returns a list of some objects that implement a DataInterface, but won't tell you their concrete type. This is often done in the API design to isolate implementation details from the interface. The convention usually is, that you can pass a list of objects, returned by the API to other API methods, and they will accept and handle them. This concept is called existential types.
Also, note that getData from the above example can return lists of different types, depending on some condition, that is outside of the caller's domain, unlike if it was declared to return List<E>, in which case, the caller would have to specify the expected type in some way.
I was looking at the Java Generics documentation and found this piece of code,
public class WildcardError {
void foo(List<?> l) {
//This give a compile time error
l.set(0,l.get(0));
}
}
I can understand that we are fetching an element from a List<?> and trying to set it to another List<?>. So the compiler gives an error. My question is it makes sense when the 2 lists are different i.e. l.set(0, m.get(0)) here lists l and m are different. But in the above example, l and l are the same lists. Why isn't the compiler smart enough to see that? Is it hard to implement it?
Edit:
I am aware that I can fix it by a helper method or by using T instead of a ?. Just wondering why compiler doesn't do it for me.
In your specific case, you can explicitly fix this:
public class WildcardError {
<T> void foo(List<T> l) {
// This will work
l.set(0, l.get(0));
}
}
Or if you don't want to change the original API, introduce a delegate helper method:
public class WildcardError {
void foo(List<?> l) {
foo0(l);
}
private <T> void foo0(List<T> l) {
// This will work
l.set(0, l.get(0));
}
}
Unfortunately, the compiler cannot infer that "obvious" <T> type. I've been wondering about that, too. It seems like something that could be improved in a compiler, as every wild card can be informally translated to an unknown <T> type. Probably, there are some reasons why this was omitted, perhaps this is only intuition, but formally impossible.
UPDATE:
Note, I've just seen this peculiar implementation of Collections.swap():
public static void swap(List<?> list, int i, int j) {
final List l = list;
l.set(i, l.set(j, l.get(i)));
}
The JDK guys resort to a raw type, in order to handle this, locally. This is a strong statement indicating that this probably should be supported by the compiler, but for some reason (e.g. lack of time to formally specify this) just wasn't done
The compiler reports an error because there is no way -- in general -- that it can tell whether two expressions, (in this case l and l) refer to the same list.
Related, somewhat generalized, question:
How does the JLS specify that wildcards cannot be formally used within methods?
List<?> means list containing elements of some unknown type, so when one wants to take elements from it using list.get(i) it will return object of some unknown type, so the only valid guess will be Object. Then when one tries to set element back using list.set(index, list.get(index)) it produces compile-time error, since as mentioned above List<?> can only contain some unknown type, so putting Object to it may cause ClassCastException.
This is explained very well in Joshua Bloch's Effective Java, 2nd ed., Item 28: Use bounded wildcards to increase API flexibility
This is also known as PECS principle and good explanation can be found in this Q/A:
What is PECS (Producer Extends Consumer Super)? (please note that List<?> is the same as List<? extends Object> with minor exceptions)
In laymans terms, one should use List<?> as method parameter only to get elements from it inside that method, not when one needs to put elements into list. When one needs to both put and get he/she needs to either generify method using type parameter T as in Lukas Eder's answer (type-safe way) or simply use List<Object> (not type-safe way).
Why do we lose type safety when using List and not while using List<Object>? Aren't they basically the same thing?
EDIT: I found that the following gives a compilation error
public class TestClass
{
static void func(List<Object> o, Object s){
o.add(s);
}
public static void main(String[] args){
func(new ArrayList<String>(), new Integer(1));
}
}
whereas this doesn't
public class TestClass
{
static void func(List o, Object s){
o.add(s);
}
public static void main(String[] args){
func(new ArrayList<String>(), new Integer(1));
}
}
Why?
List is a list of some type you don't know. It could be a List<String>, List<Integer>, etc.
It's effectively equivalent to List<?>, or List<? extends Object>, except that it doesn't document that fact. It's only supported for backwards compatibility.
List<Object> is a list of Objects. Any object of any type can be put inside it, contrary to a List<String>, for example, which only accepts strings.
So no, they're not the same thing.
Why do we lose type safety when using List and not while using List<Object>? Aren't they basically the same thing?
No they are not the same thing.
If you are providing an API,
class API {
static List<Object> getList() { ... }
static void modifyList(List<Object> l) { ... }
}
and a client uses it improperly
List<Integer> list = API.getList();
API.modifyList(list);
for (Integer i : list) { ... } // Invalid
then when your API specifies List<Object> they get a compile-time error, but they don't when API.getList() returns a List and API.modifyList(list) takes a List without generic type parameters.
EDIT:
In comments you mentioned changing
void func(List<Object> s, Object c) { s.add(c); }
to
void func(List s, Object c) { s.add(c); }
so that
func(new List<String>(), "");
would work.
That is violating type safety. The type-safe way to do this is
<T> void func(List<? super T> s, T c) { s.add(c); }
which is basically saying that func is a parameterized function that takes a List whose type can be any super class of T, and a value of type T, and adds the value to the list.
A List<Object> isn't really any more typesafe than a List. However, the Object in the code does imply intent. When someone else looks at it later, they can see that you purposefully chose Object as the type, rather than wondering if you just forgot to put a type or are storing something else and typecasting it elsewhere.
Since code gets read more than it gets written, hints at the intent of the code can be very valuable later on.
The reason you have a compiler warning when you use List instead of List<Object> is that when you have a List the compiler doesn't know what type of List it is, so while you could treat it as a List<Object>, the compiler can't ensure that at some other point in the code it wasn't set to reference a List<String> and the type safety of the Generics cannot be checked. That is really the compiler warning here - it is saying it can't help ensure the type safety of the generics, and it won't happen at runtime either (until at some later point there is an actual cast in the code).
For purposes here, you could say they're the same thing. But presumably you almost never actually fill a List with pure instance of Object. They're Strings or something. In this example, List<Object> is technically using generics but not really taking any advantage of it. So, it loses the compile-time type checking of generics.
Type Erasure is one answer and the backward compatibility to pre Java 1.5 and tighter type check in case of first one.
Generics were introduced to the Java language to provide tighter type checks at compile time and to support generic programming. To implement generics, the Java compiler applies type erasure to:
Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.
Insert type casts if necessary to preserve type safety.
Generate bridge methods to preserve polymorphism in extended generic types.
Type erasure ensures that no new classes are created for parameterized types; consequently, generics incur no runtime overhead.
Why is it not legal to have the following two methods in the same class?
class Test{
void add(Set<Integer> ii){}
void add(Set<String> ss){}
}
I get the compilation error
Method add(Set) has the same erasure add(Set) as another method in type Test.
while I can work around it, I was wondering why javac doesn't like this.
I can see that in many cases, the logic of those two methods would be very similar and could be replaced by a single
public void add(Set<?> set){}
method, but this is not always the case.
This is extra annoying if you want to have two constructors that takes those arguments because then you can't just change the name of one of the constructors.
This rule is intended to avoid conflicts in legacy code that still uses raw types.
Here's an illustration of why this was not allowed, drawn from the JLS. Suppose, before generics were introduced to Java, I wrote some code like this:
class CollectionConverter {
List toList(Collection c) {...}
}
You extend my class, like this:
class Overrider extends CollectionConverter{
List toList(Collection c) {...}
}
After the introduction of generics, I decided to update my library.
class CollectionConverter {
<T> List<T> toList(Collection<T> c) {...}
}
You aren't ready to make any updates, so you leave your Overrider class alone. In order to correctly override the toList() method, the language designers decided that a raw type was "override-equivalent" to any generified type. This means that although your method signature is no longer formally equal to my superclass' signature, your method still overrides.
Now, time passes and you decide you are ready to update your class. But you screw up a little, and instead of editing the existing, raw toList() method, you add a new method like this:
class Overrider extends CollectionConverter {
#Override
List toList(Collection c) {...}
#Override
<T> List<T> toList(Collection<T> c) {...}
}
Because of the override equivalence of raw types, both methods are in a valid form to override the toList(Collection<T>) method. But of course, the compiler needs to resolve a single method. To eliminate this ambiguity, classes are not allowed to have multiple methods that are override-equivalent—that is, multiple methods with the same parameter types after erasure.
The key is that this is a language rule designed to maintain compatibility with old code using raw types. It is not a limitation required by the erasure of type parameters; because method resolution occurs at compile-time, adding generic types to the method identifier would have been sufficient.
Java generics uses type erasure. The bit in the angle brackets (<Integer> and <String>) gets removed, so you'd end up with two methods that have an identical signature (the add(Set) you see in the error). That's not allowed because the runtime wouldn't know which to use for each case.
If Java ever gets reified generics, then you could do this, but that's probably unlikely now.
This is because Java Generics are implemented with Type Erasure.
Your methods would be translated, at compile time, to something like:
Method resolution occurs at compile time and doesn't consider type parameters. (see erickson's answer)
void add(Set ii);
void add(Set ss);
Both methods have the same signature without the type parameters, hence the error.
The problem is that Set<Integer> and Set<String> are actually treated as a Set from the JVM. Selecting a type for the Set (String or Integer in your case) is only syntactic sugar used by the compiler. The JVM can't distinguish between Set<String> and Set<Integer>.
Define a single Method without type like void add(Set ii){}
You can mention the type while calling the method based on your choice. It will work for any type of set.
It could be possible that the compiler translates Set(Integer) to Set(Object) in java byte code. If this is the case, Set(Integer) would be used only at compile phase for syntax checking.
I bumped into this when tried to write something like:
Continuable<T> callAsync(Callable<T> code) {....}
and
Continuable<Continuable<T>> callAsync(Callable<Continuable<T>> veryAsyncCode) {...}
They become for compiler the 2 definitions of
Continuable<> callAsync(Callable<> veryAsyncCode) {...}
The type erasure literally means erasing of type arguments information from generics.
This is VERY annoying, but this is a limitation that will be with Java for while.
For constructors case not much can be done, 2 new subclasses specialized with different parameters in constructor for example.
Or use initialization methods instead... (virtual constructors?) with different names...
for similar operation methods renaming would help, like
class Test{
void addIntegers(Set<Integer> ii){}
void addStrings(Set<String> ss){}
}
Or with some more descriptive names, self-documenting for oyu cases, like addNames and addIndexes or such.
In this case can use this structure:
class Test{
void add(Integer ... ii){}
void add(String ... ss){}
}
and inside methods can create target collections
void add(Integer ... values){
this.values = Arrays.asList(values);
}
I saw a java function that looked something like this-
public static<T> foo() {...}
I know what generics are but can someone explain the in this context? Who decides what T is equal to? Whats going on here?
EDIT: Can someone please show me an example of a function like this.
You've missed the return type out, but apart from that it's a generic method. As with generic types, T stands in for any reference type (within bounds if given).
For methods, generic parameters are typically inferred by the compiler. In certain situations you might want to specify the generic arguments yourself, using a slightly peculiar syntax:
List<String> strings = Collections.<String>emptyList();
In this case, the compiler could have inferred the type, but it's not always obvious whether the compiler can or can't. Note, the <> is after the dot. For syntactical reasons the type name or target object must always be specified.
It's possible to have generic constructors, but I've never seen one in the wild and the syntax gets worse.
I believe C++ and C# syntaxes place the generic types after the method/function name.
The context is a generic method as opposed to a class. The variable <T> applies only to the call of the method.. The Collections class has a number of these; the class itself is not generic, but many of the methods are.
The compiler decides what T is equal to -- it equals whatever gets the types to work. Sometimes this is easier then others.
For example, the method static <T> Set<T> Collections.singleton(T o) the type is defined in the parameter:
Collections.singleton(String T)
will return a Set<String>.
Sometimes the type is hard to define. For example sometimes there is not easily enough information to type Collection.emptyList(). In that case you can specify the type directly: Collection.<String>emptyList().
T it's the formal type parameter wich will be replaced by the actual type
argument used at the instantiation of the object.
For example, here is the List and Iterator definitios in package java.util:
public interface List<E>{
void add(E x);
Iterator<E> iterator();
}
public interface Iterator<E>{
E next();
boolean hasNext();
}
Then you can instantiate a List this way:
List<String> ls = new ArrayList<String>()
Where you might imagine that List stands for a version of List where E has
been uniformly replaced by String:
public interface StringList{
void add(String x)
Iterator<String> iterator();
}