Description

Generics allow us to parameterize interfaces, classes, and methods in Java. Typically, generics are used in conjunction with containers. A container is a class used to hold objects in some meaningful way (e.g. an ordered list, a set).

Here is an example of a generic class:

class SomeClass<T>
{
	private T someInstanceVariable;

	public SomeClass( T someVariable )
	{
		someInstanceVariable = someVariable;
	}

	public T getSomeInstanceVariable()
	{
		return someInstanceVariable;
	}
}

When creating a generic object, you may now specify the actual type of T:

SomeClass<String> someObject = new SomeClass<String>( "A String" );

Now every T in the class definition will be the specified parameter type. In the above example, T would be String. Typically, we would like to be able to do things with a generic type, but in the above example, we don't know anything about T, except that it will be an Object. So, we can call Object methods, like toString and equals. But what if we want to be more specific about the actual type of T?

We may "bound" a generic type to gurantee some associated behavior (available methods):

class SomeClass<T extends Comparable<T>>
{
	private T someInstanceVariable;

	public SomeClass( T someVariable )
	{
		someInstanceVariable = someVariable;
	}

	public T getSomeInstanceVariable()
	{
		return someInstanceVariable;
	}
	
	public boolean isLessThan( SomeClass<T> someArgument )
	{
		if( someInstanceVariable.compareTo( someArgument.getSomeInstanceVariable() ) < 0 )
			return true;
		return false;
	}
	 public static void main(String[] args) {
		SomeClass s1=new SomeClass("A");
		SomeClass s2=new SomeClass("B");
		System.out.println("s1 less than s2?? "+s1.isLessThan(s2));
		
		SomeClass i1=new SomeClass(200);
		SomeClass i2=new SomeClass(120);
		System.out.println("i1 less than i2?? "+i1.isLessThan(i2));
	}
}

Now we have bounded type T to be a Comparable. Now in the isLessThan method, it is safe to assume that T has a compareTo method. Notice that even though Comparable is an interface, we use the "extends" keyword. "extends" in generics can also mean that a generic class type will implement a particular interface it is "extending". Also, the power of generics can be seen in the simple main method above, where we used a single class 'SomeClass' to compare both integers and strings without differentiating between the data types like we would have done otherwise by overloading the 'isLessThan' method to treat strings and integers separately.

We may also define generic interfaces and generic methods. Generic interfaces follow the same pattern as the above class example, but generic methods look a little different:

public <U extends Comparable<U>> void someMethod( SomeClass<T> someArgument)
// ...

You may also specify more than one generic type or bound a type by more than one interface:

Like this class takes two generic parameters without any constraint on any of them:
class SomeClass<T , S>
However this class takes two generic parameters with a constraint that the first class must implement the Comparable interface:
class SomeClass<T extends Comparable<T>, S>
Notice the use of '&' in the class declaration below. The class which may look like taking two arguments actually takes in just one, with a constraint that the generic parameter class passed in must implement two interfaces: 'Comparable' and 'SomeOtherInterface':
class SomeClass<T extends Comparable<T> & SomeInterface<T>>

This lab will try to create a list of catalog items and sort it. The item may be a 'Book' or a 'CD'. Both 'Book' and 'CD' will therefore be classes implementing an interface called 'Media'. Since the catalog item can be a 'Book' or a 'CD', we will make a generic class called 'CatalogItem' which can take one of these two types.

The details of the lab are as follows: