My Avatar

Shilong ZHAO

Java Generics -- A Quick Tutorial

2017-04-05 00:00:00 +0200

In case you have any questions or suggestions, you can leave comments HERE . Thanks!

Why Generics?

Suppose we need to write classes represent stores. We have dessert store Pasticceria, bread store Panetteria, meat store Macelleria and stationary store Cartoleria and many more. In the world without Generics, one would have to write different classes each representing a specific store, like this:

public class Paticceria {
	Dessert[] desserts;
}

public class Panetteria {
	Bread[] breads;
}

public class Macelleria {
	Meat[] meats;
}

Well, this is so wordy that I give up to write the Cartoleria. Is there any solution to this? Smart as you may think why not use an array of Objects to store the merchandises. Yes we can.

public class Store {
	Object[] merchandises;
	...
	public void add(Object m) {
		...
	}
}

Now only one class is needed. Voila! we are saved from writing a bunch of classes. Now we can write a client that uses the Store class:

public class StoreClient {
	public static void main(String[] args) {
		Store pasticceria = new Store();
		Store marcelleria = new Store();
		pasticceria.add(new Dessert("Tiramisu"));
		pasticceria.add(new Dessert("Cannolo"));
		pasticceria.add(new Meat("beef"));
		...
	}
}

How delicious these desserts… But wait! Did someone just add a piece of beef to my desserts?! The Object array makes life simpler. However, it will not save you from the type mismatch. Since every class in Java inherits from Object, you can add anything inside the pasticceria or marcelleria. Now we can see that, if we use the more wordy solution, i.e. one class for each store, the type match will be guaranteed. If we use the shorter one, i.e. Object array, we lose the type constraint. Is there a solution so that the code is short and the type is checked? Yes, that’s when Generics come to save.

public class GenericStore<T> {
	T[] merchandise = (T[]) new Object[100];
}

You might have noticed we are not directly initializing an array of T with T[] merchandise = new T[] but instead we create an Object array and then cast it to T. That is because of the mechanism called Erasure; we will talk about it in more details soon. With GenericStore, we create a pasticceria and never has to worry that there will be raw meat in it at runtime.

public class GenericStoreClient {
	public static void main(String[] args) {
		GenericStore<Dessert> pasticceria = new GenericStore<>();
		pasticceria.add(new Dessert("Tiramisu"));
		pasticceria.add(new Dessert("Cannolo"));
		pasticceria.add(new Meat("beef"));	// Ooops, compile time error!
	}
}

A store sells only vendible

It may not be a good idea to let a store sell everything they want, and we want to make sure that a store is selling only what are Vendible. This can be achieved by setting an upper bound of the type variable T.

public interface Vendible {
...
}
public class GenricStore<T extends Vendible> {
...
}

Generic Interfaces

What’s the difference <?> and

Upper bounds <? extends A> and lower bounds <? super A>

What is <T extends Comparable<? super T» and why so wordy?

import java.util.List;

/**
 * author: zhaoshilong
 * date: 05/04/2017
 */
// public class SortElements<T extends Comparable<T>> {
public class SortElements<T extends Comparable<? super T>> {

    public void sort(List<T> elements) {
    }

    public static void main(String[] args) {
        SortElements<AA> sortAA = new SortElements<>();
        SortElements<BB> sortBB = new SortElements<BB>();
    }
}

class AA implements Comparable<AA> {
    @Override
    public int compareTo(AA o) {
        return 0;
    }
}

class BB extends AA {

}

class CC extends BB {

}

Type erasure and when we cannot use type variables