Byron Kiourtzoglou

About Byron Kiourtzoglou

Byron is a master software engineer working in the IT and Telecom domains. He is always fascinated by SOA, middleware services and mobile development. Byron is co-founder and Executive Editor at Java Code Geeks.

Java Generics examples

The Motivation for Generics

The simplest way to think about Java generics is thinking about a sort of a syntactic sugar that might spare you some casting operation:

List<Apple> box = ...;
Apple apple = box.get(0);

The previous code is self-speaking: box is a reference to a List of objects of type Apple. The get method returns an Apple instance an no casting is required.

 

Generic Classes and Interfaces

A class or an interface is generic if it has one or more type variable. Type variable are delimited by angle brackets and follow the class (or the interface) name:

public interface List<T> extends Collection<T> {
...
}

Roughly speaking, type variables act as parameters and provide the information the compiler needs to make its checks.

Generic Methods and Constructors

Pretty much the same way, methods and constructors can be generic if they declare one or more type variables.

public static <t> T getFirst(List<T> list)

This method will accept a reference to a List<T> and will return an object of type T.

Examples

You can take advantage of generics in both your own classes or the generic Java library classes.

Type Safety When Writing…

In the following code snippet, for example, we create an instance List<String> of populate it with some data:

List<String> str = new ArrayList<String>();
str.add("Hello ");
str.add("World.");

If we tried to put some other kind of object into the List<String>, the compiler would raise an error:

str.add(1); // won't compile

… and When Reading

If we pass the List<String> reference around, we’re always guaranteed to retrieve a String object from it:

String myString = str.get(0);

Iterating

Many classes in the library, such as Iterator<T>, have been enhanced and made generic. The iterator() method of the interface List<T> now returns an Iterator<T> that can be readily used without casting the objects it returns via its T next() method.

for (Iterator<String> iter = str.iterator(); iter.hasNext();) {
String s = iter.next();
System.out.print(s);
}

Using foreach

The for each syntax takes advantage of generics, too. The previous code snippet could be written as:

for (String s: str) {
System.out.print(s);
}

Autoboxing and Autounboxing

The autoboxing/autounboxing features of the Java language are automatically used when dealing with generics, as shown in this code snippet:

List<Integer> ints = new ArrayList<Integer>();
ints.add(0);
ints.add(1);
 
int sum = 0;
for (int i : ints) {
sum += i;
}

Be aware, however, that boxing and unboxing come with a performance penalty so the usual caveats and warnings apply.

 

Subtyping of Generic Types

If a reference of an Apple instance can be assigned to a reference of a Fruit (Apple is a subtype of Fruit), then what’s the relation between, let’s say, a List<Apple> and a List<Fruit>? Which one is a subtype of which? More generally, if a type A is a subtype of a type B, how does C<A> and C<B> relate themselves?

Surprisingly, the answer is: in no way. In more formal words, the subtyping relation between generic types is invariant.

This means that the following code snippet is invalid:

List<Apple> apples = ...;
List<Fruit> fruits = apples;

and so does the following:

List<Apple> apples;
List<Fruit> fruits = ...;
apples = fruits;

But why? If an apple is a fruit, a box of apples (a list) is also a box of fruits.

In some sense, it is, but types (classes) encapsulate state and operations. What would happen if a box of apples was a box of fruits?

List<Apple> apples = ...;
List<Fruit> fruits = apples;
fruits.add(new Strawberry());

If it was, we could add other different subtypes of Fruit into the list and this must be forbidden.

The other way round is more intuitive: a box of fruits is not a box of apples, since it may be a box (List) of other kinds (subtypes) of fruits (Fruit), such as Strawberry.

 

Covariance

Let’s suppose, for example, that we’ve got a set of boxes, each one of a different kind of fruit. We’d like to be able to write methods that could accept any of them. More formally, given a subtype A of a type B, we’d like to find a way to use a reference (or a method parameter) of type C<B> that could accept instances of C<A>.

To accomplish this task we can use a wildcard with extends, such as in the following example:

List<Apple> apples = new ArrayList<Apple>();
List<? extends Fruit> fruits = apples;

? extends reintroduces covariant subtyping for generics types: Apple is a subtype of Fruit and List<Apple> is a subtype of List<? extends Fruit>.

Contravariance

Let’s now introduce another wildcard: ? super. Given a supertype B of a type A, then C<B> is a subtype of C<? super A>:

List<Fruit> fruits = new ArrayList<Fruit>();
List<? super Apple> = fruits;

 

How Can Wildcards Be Used?

? extends

Let’s go back to the example we used in Part II when introducing Java array covariance:

Apple[] apples = new Apple[1];
Fruit[] fruits = apples;
fruits[0] = new Strawberry(); 

As we saw, this code compiles but results in a runtime exception when trying to add a Strawberry to an Apple array through a reference to a Fruit array.

Now we can use wildcards to translate this code to its generic counterpart: since Apple is a subtype of Fruit, we will use the ? extends wildcard to be able to assign a reference of a List<Apple> to a reference of a List<? extends Fruit> :

List<Apple> apples = new ArrayList<Apple>();
List<? extends Fruit> fruits = apples;
fruits.add(new Strawberry());

This time, the code won’t compile! The Java compiler now prevents us to add a strawberry to a list of fruits. We will detect the error at compile time and we won’t even need any runtime check (such as in the case of array stores) to ensure that we’re adding to the list a compatible type. The code won’t compile even if we try to add a Fruit instance into the list:

fruits.add(new Fruit());

No way. It comes out that, indeed, you can’t put anything into a structure whose type uses the ? extends wildcard.

The reason is pretty simple, if we think about it: the ? extends T wildcard tells the compiler that we’re dealing with a subtype of the type T, but we cannot know which one. Since there’s no way to tell, and we need to guarantee type safety, you won’t be allowed to put anything inside such a structure. On the other hand, since we know that whichever type it might be, it will be a subtype of T, we can get data out of the structure with the guarantee that it will be a T instance:

Fruit get = fruits.get(0);

? super

What’s the behavior of a type that’s using the ? super wildcard? Let’s start with this:

List<Fruit> fruits = new ArrayList<Fruit>();
List<? super Apple> = fruits;

We know that fruits is a reference to a List of something that is a supertype of Apple. Again, we cannot know which supertype it is, but we know that Apple and any of its subtypes will be assignment compatible with it. Indeed, since such an unknown type will be both an Apple and a GreenApple supertype, we can write:

fruits.add(new Apple());
fruits.add(new GreenApple());

If we try to add whichever Apple supertype, the compiler will complain:

fruits.add(new Fruit());
fruits.add(new Object());

Since we cannot know which supertype it is, we aren’t allowed to add instances of any.

What about getting data out of such a type? It turns out that you the only thing you can get out of it will be Object instances: since we cannot know which supertype it is, the compiler can only guarantee that it will be a reference to an Object, since Object is the supertype of any Java type.

 

Wildcards in Method Signatures

If, for example, a developer wanted to define a method eat that accepted a List of whichever fruit, it could use the following signature:

void eat(List<? extends Fruit> fruits);

On the other hand, if you wanted to put instances on the list passed as a parameter, you should use the ? super wildcard:

void store(List<? super Fruit> container);

This way, a List of whichever supertype of Fruit could be passed in to the store function and you could safely put whichever Fruit subtype into it.

Bounded Type Variables

The flexibility of generics is greater than this, though. Type variables can be bounded, pretty much in the same way wildcards can be (as we’ve seen in Part II). However, type variables cannot be bounded with super, but only with extends. Look at the following signature:

public static <T extends I<T>> void name(Collection<T> t);

It takes a collections of objects whose type is bounded: it must satisfy the T extends I<T> condition. Using bounded type variables may not seem more powerful than wildcards at first, but we’ll detail the differences in a moment.

Let’s suppose some, but not all, fruits in your hierarchy can be juicy as in:

public interface Juicy<T> {
    Juice<T> squeeze();
}

Juicy fruits will implement this interface and publish the squeeze method.

Now, you write a library method that takes a bunch of fruits and squeezes them all. The first signature you could write might be:

<T> List<Juice<T>> squeeze(List<Juicy<T>> fruits);

Using bounded type variables, you would write the following (which, indeed, has got the same erasure of the previous method):

<T extends Juicy<T>> List<Juice<T>> squeeze(List<T> fruits);

So far, so good. But limited. We could use the very same arguments used in the same posts and discover that the squeeze method is not going to work, for example, with a list of red oranges when:

class Orange extends Fruit implements Juicy<Orange>;
class RedOrange extends Orange;

We’re going to change the method with:

<T extends Juicy<? super T>> List<Juice<? super T>> squeezeSuperExtends(List<? extends T> fruits);

This method accepts a list of objects whose type extends Juicy<? super T>, that is, in other words, that there must exist a type S such that T extends Juicy<S> and S super T.

Recursive Bounds

Maybe you feel like relaxing the T extends Juicy<? super T> bound. This kind of bound is called recursive bound because the bound that the type T must satisfy depends on T. You can use recursive bounds when needed and also mix-and-match them with other kinds of bounds.

<A extends B<A,C>, C extends D<T>>

Please remember that these examples are only given to illustrate what generics can do. Bounds you’re going to use always depend on the constraints you’re putting into your type hierarchy.

Using Multiple Type Variables

Let’s suppose you want to relax the recursive bound we put on the last version of the squeeze method. Let’s then suppose that a type T might extend Juicy<S> although T itself does not extends S. The method signature could be:

<T extends Juicy<S>, S> List<Juice<S>> squeezeSuperExtendsWithFruit(List<? extends T> fruits);

This signature has pretty much equivalent to the previous one (since we’re only using T in the method arguments) but has got one slight advantage: since we’ve declared the generic type S, the method can return List<Juice<S> instead of List<? super T>, which can be useful in some situations, since the compiler will help you identify which type S is according to the method arguments you’ve passed. Since you’re returning a list, chances are you want your caller to be able to get something from it and, as you’ve learned in the previous part, you can only get Object instances from a list such as List<? super T>.

You can obviously add more bounds to S, if you need them, such as:

<T extends Juicy<S>, S extends Fruit> List<Juice<S>> squeezeSuperExtendsWithFruit(List<? extends T> fruits);

 

Multiple Bounds

Multiple bounds must be expressed with a different syntax, which turns out to be a pretty familiar notation:

<T extends A & B>

The previous bounds means that T extends both A and B. Please take into account that multiple bounds can only be expressed using interface types.

Related Article:

Reference: Series of articles about Java Generics from our JCG partner Gray at The Grey Blog

Related Whitepaper:

Java Essential Training

Author David Gassner explores Java SE (Standard Edition), the language used to build mobile apps for Android devices, enterprise server applications, and more!

The course demonstrates how to install both Java and the Eclipse IDE and dives into the particulars of programming. The course also explains the fundamentals of Java, from creating simple variables, assigning values, and declaring methods to working with strings, arrays, and subclasses; reading and writing to text files; and implementing object oriented programming concepts. Exercise files are included with the course.

Get it Now!  

Examples Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use
All trademarks and registered trademarks appearing on Examples Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Examples Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

15,153 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books
Get tutored by the Geeks! JCG Academy is a fact... Join Now
Hello. Add your message here.