Next page | Contents page |

More on generics

Consider this code:

	List <String> sList = new ArrayList <String> ();
	List <Object> oList = sList;
	oList.add (new Object ()); 
	String s = sList.get (0); // Assign Object to String!

The compiler prevents line 2. List <Object> is not a superclass of List <String>. A reasonable mistake to make!

Generics coexist with legacy code, so you can still declare and use a non-generic "raw type" List but it is not the same thing as List <Object> because the latter enables stricter compiler checking.

<?> = "of type unknown"

	void printList (List <Object> list)
	{
		for (Object obj : list)
		{
			System.out.println (obj);
		}
	}

This method will only take a List <Object>, not a List <String>, which is rather restrictive. Instead we can declare the method as

	void printList (List <?> list)
	{
		for (Object obj : list) 
		{
			System.out.println (obj);
		}
	}

However, be careful inside the method. Cannot use

    list.add (new Object ());

because the List <?> will have some particular type, not necessarily Object. On the other hand, list.get () does return an Object, as always, so it is safe to use.

Bounded wildcards

It is not hard to construct examples where you don't want the complete wildcard <?> but only a certain range of types is acceptable. This can be expressed with, eg

	List <? extends Person>  /* upper bounded wild-card */

Sun's generics tutorial (see the API documentation) shows that this is sometimes useful too:

	List <? super MyClass> /* lower bounded wild-card */

NB: In both cases the named class itself is acceptable as the type.

Type erasure

Java byte code did not change when generics were introduced. Generics are entirely for the compiler. So

	public class Account <T extends Customer>
	{
	   public void sendInvoice (T customer)
	   {
	      //...

is compiled to

	public class Account
	{
	   public void sendInvoice (Customer customer)
	   {
	      //...

If T did not extend anything then the erased type would be Object. If unsure of behaviour involving generics, thinking about type erasure can sometimes help.

Raw types

The JVM only knows the raw type, so the run-time test

    x instanceof List <Customer>

only tests whether x is a List, of any type, and

   List <Customer> customers = ...;
   List <String> names = ...;

   if (customers.getClass () == names.getClass ())
   {
	// will be true: getClass () returns java.util.List
   }

You cannot cast with a generic type:

	List <String> s = (List <String>) x; // -> compiler warning
Next page | Contents page |