We’ve seen that generic code can take type arguments to help generalize our code. We can make our code even more general when we don’t need the more strict type checking of using type parameters by using wildcards. A wildcard, denoted by the ?
symbol, represents an unknown type when used with generic methods. Let’s look at an example:
public class Util { public static void printBag(Bag<?> bag) { System.out.println(bag.toString()); } } Bag<String> myBag1 = new Bag("Hello"); Bag<Integer> myBag2 = new Bag(23); Util.printBag(myBag1); // Hello Util.printBag(myBag2); // 23
In the example above, we defined a static
generic method that works on Bag
types. We specify that Bag
, which is a generic class, can be of any type by using the wildcard Bag<?>
. We also created two Bag
references of different types, String
and Integer
, and used them as arguments to printBag()
.
You may be thinking how this is different than using a type parameter because we could write the above method as:
public static <T> void printBag(Bag<T> bag) { System.out.println(bag.toString()); }
In the example above, we’ve defined the parameter as Bag<T>
as opposed to Bag<?>
. This implementation makes no difference, but you may notice that the wildcard version is better as it does not need the extra <T>
before the return type making the signature easier to read.
In general, we should use type parameters, when we have a relationship between the type of arguments and the return type. For example:
public static <T> Bag<T> getBag(Bag<T> bag) { return bag; }
In the example above, we created a method that accepts a Bag
of some type T
and returns a Bag
of the same type T
. This use of type parameters maintains strong type checking throughout our code.
We can also create upper bounds and lower bounds (we’ll see this later) when using wildcards. For example:
public static void printBag(Bag<? extends Number> bag) { System.out.println(bag.toString()); }
In the example above, printBag()
accepts any Bag
that is a Number
and will produce an error when passing a type of Bag
that does not extend Number
. For example, Bag<String>
will produce an error.
Let’s practice using wildcards in our generic code.
Instructions
Take a look at the code in the editor to see how wildcards can be used with our Container
class.