Difference Between Generic Type and Wildcard Type

Difference between generic type and wildcard type

The first signature says: list1 is a List of Es.

The second signature says: list is a List of instances of some type, but we don't know the type.

The difference becomes obvious when we try to change the method so it takes a second argument, which should be added to the list inside the method:

import java.util.List;

public class Experiment {
public static <E> void funct1(final List<E> list1, final E something) {
list1.add(something);
}

public static void funct2(final List<?> list, final Object something) {
list.add(something); // does not compile
}
}

The first one works nicely. And you can't change the second argument into anything that will actually compile.

Actually I just found an even nicer demonstration of the difference:

public class Experiment {
public static <E> void funct1(final List<E> list) {
list.add(list.get(0));
}

public static void funct2(final List<?> list) {
list.add(list.get(0)); // !!!!!!!!!!!!!! won't compile !!!!!!!!!
}
}

One might as why do we need <?> when it only restricts what we can do with it (as @Babu_Reddy_H did in the comments). I see the following benefits of the wildcard version:

  • The caller has to know less about the object he passes in. For example if I have a Map of Lists: Map<String, List<?>> I can pass its values to your function without specifying the type of the list elements. So

  • If I hand out objects parameterized like this I actively limit what people know about these objects and what they can do with it (as long as they stay away from unsafe casting).

These two make sense when I combine them: List<? extends T>. For example consider a method List<T> merge(List<? extends T>, List<? extends T>), which merges the two input lists to a new result list. Sure you could introduce two more type parameters, but why would you want to? It would be over specifying things.

  • finally wildcards can have lower bounds, so with lists you can make the add method work, while get doesn't give you anything useful. Of course that triggers the next question: why don't generics have lower bounds?

For a more in depth answer see: When to use generic methods and when to use wild-card? and http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeArguments.html#FAQ203

Difference between ? (wildcard) and Type Parameter in Java

They are the same in that they accept the same parameter types.

However, identifying the type with T (or whatever) lets you refer to the type elsewhere.

Edit: Examples:

Your unbounded examples do not make full use of the capabilities of parameterized types. You have:

public static <T> void printList(List<T> list) {
for (Object elem : list)
System.out.println(elem + " ");
System.out.println();
}

And that's sufficient for that example of printing string representations, but consider this (very contrived, and no error handling):

public static <T> T getSecondItem (List<T> list) {
T item = list.get(1);
return item;
}

The return type is T, which allows you to safely do things like this, with compile time type-checking:

class MyClass {
public void myMethod () { }
}

void somewhere () {
List<MyClass> items = ...;
getSecondItem(items).myMethod();
}

A named type also lets you share the same type constraint in multiple places, e.g.:

public <T> int compareLists (List<T> a, List<T> b) {
...
}

If you did not name the type, you could not specify the constraint that a and b are the same list type (you could use List<? extends T> for more flexibility).

You also asked "Why do I need ??". The real answer is: You don't. It's mostly for aesthetics, I suppose. Java strives to be a precise and clutter-free language. There are many situations where you simply don't care what type you are referring to. In those cases, you may use ? without cluttering code with unused type parameter declarations.

Java generics: wildcard? vs type parameterE?

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

When to use generic methods and when to use wild-card?

There are certain places, where wildcards, and type parameters do the same thing. But there are also certain places, where you have to use type parameters.

  1. If you want to enforce some relationship on the different types of method arguments, you can't do that with wildcards, you have to use type parameters.

Taking your method as example, suppose you want to ensure that the src and dest list passed to copy() method should be of same parameterized type, you can do it with type parameters like so:

public static <T extends Number> void copy(List<T> dest, List<T> src)

Here, you are ensured that both dest and src have same parameterized type for List. So, it's safe to copy elements from src to dest.

But, if you go on to change the method to use wildcard:

public static void copy(List<? extends Number> dest, List<? extends Number> src)

it won't work as expected. In 2nd case, you can pass List<Integer> and List<Float> as dest and src. So, moving elements from src to dest wouldn't be type safe anymore.
If you don't need such kind of relation, then you are free not to use type parameters at all.

Some other difference between using wildcards and type parameters are:

  • If you have only one parameterized type argument, then you can use wildcard, although type parameter will also work.
  • Type parameters support multiple bounds, wildcards don't.
  • Wildcards support both upper and lower bounds, type parameters just support upper bounds. So, if you want to define a method that takes a List of type Integer or it's super class, you can do:

    public void print(List<? super Integer> list)  // OK

    but you can't use type parameter:

     public <T super Integer> void print(List<T> list)  // Won't compile

References:

  • Angelika Langer's Java Generics FAQs

Difference between raw types and ? in Generics

ArrayList<?> simply means "any type." In other words, any type of ArrayList can be assigned to such variable. That could be ArrayList<Integers>, ArrayList<JButton> or anything else. Using the wildcard alone, without the keyword super (followed by a type), means that you cannot ADD anything to the list defined as ArrayList<?>. ArrayList alone however, means the old style type-less ArrayList which you can do all sorts of operations including add.

List<?> list;
List<Integer> ints = new ArrayList<Integer>();
List<Integer> strings = new ArrayList<Integer>();
list = ints; // valid
list = strings; // valid
list.add("new"); // compile error

UPDATE:

Suppose I have following method:

void insert(List list) {
// loop through list, do whatever you like
list.add("my string"); // dangerous operation
}

Now if I call insert(ints) compiler will generate a warning but will not prevent me of adding a String to a list of integers. Changing method to following:

void insert(List<?> list) {
// loop through list, do whatever you like
list.add("my string"); // compiler error on this dangerous operation
}

would prevent me of doing such an operation.

Differences between Wildcard and generic methods? [Java]

A wildcard in Java represents an unknown type and they can be used as return type. To quote the explanation given in the Oracle Java tutorial:

The wildcard can be used in a variety of situations: as the type of a parameter, field, or local variable; sometimes as a return type (though it is better programming practice to be more specific). The wildcard is never used as a type argument for a generic method invocation, a generic class instance creation, or a supertype. See this

Let's say you have a List<String> and you would try to pass it to method that accepts List<Object>, it will not compile (Java is trying to protect you from creating a runtime exception here). However, if you pass it to a method that accepts List<?> it will. This may give you a feeling about how wildcards can be useful.

The keyword extends is used for wildcards with an upper bound, e.g. List<? extends Object>. There is also the wildcard with a lower bound: List<? super String>.

Without wildcards, the whole topic of generics would be much less interesting because generics are treated as objects (type erasure). This means that there are not many methods available for them. Wildcards solve this by restricting the type (and thus a common interface, which includes a common set of methods that can be invoked on an object, is specified).

Java generics, Unbound wildcards ? vs Object

There are two separate issues here. A List<Object> can in fact take any object as you say. A List<Number> can take at least Number objects, or of course any subclasses, like Integer.

However a method like this:

public void print(List<Number> list);

will actually only take a List which is exactly List<Number>. It will not take any list which is declared List<Integer>.

So the difference is List<?> will take any List with whatever declaration, but List<Object> will only take something that was declared as List<Object>, nothing else.

The last quote simply states, that List<?> is a list for which you literally don't know what type its items are. Because of that, you can not add anything to it other than null.

Difference between Bounded Type parameter (T extends) and Upper Bound Wildcard (? extends)

There are several differences between the two syntaxes during compile time :

  • With the first syntax, you can add elements to someList but with the second, you can't. This is commonly known as PECS and less commonly known as the PUT and GET prinicple.
  • With the first syntax, you have a handle to the type parameter T so you can use it to do things such as define local variables within the method of type T, cast a reference to the type T, call methods that are available in the class represented by T, etc. But with the second syntax, you don't have a handle to the type so you can't do any of this.
  • The first method can actually be called from the second method to
    capture the wildcard. This is the most common way to capture a
    wildcard via a helper method.

    private static <T extends Number> void processList(List<T> someList) {
    T n = someList.get(0);
    someList.add(1,n); //addition allowed.
    }

    private static void processList2(List<? extends Number> someList) {
    Number n = someList.get(0);
    //someList.add(1,n);//Compilation error. Addition not allowed.
    processList(someList);//Helper method for capturing the wildcard
    }

Note that since generics are compile time sugar, these differences at a broader level are only limited to the compilation.



Related Topics



Leave a reply



Submit