Java Generics
- What is Generics?
- The Legacy Way to Do Collections
- Using Generics
- Generic Methods
- Bounded Type Parameters
- Generic Classes
What is Generics? |
- J2SE 5.0 provides compile-time type safety with the Java Collections framework through generics
- Generics allows you to specify, at compile-time, the types of objects you want to store in a Collection. Then when you add and get items from the list, the list already knows what types of objects are supposed to be acted on.
- So you don’t need to cast anything. The “<>” characters are used to designate what type is to be stored. If the wrong type of data is provided, a compile-time exception is thrown.
The Legacy Way to Do Collections |
Here’s a review of a pre-Java 5 ArrayList intended to hold Strings.
List myList = new ArrayList(); // can't declare a type
myList.add("Hello"); // OK, it will hold Strings
myList.add(new Dog()); // and it will hold Dogs too
myList.add(new Integer(42)); // and Integers...
- A non-generic collection can hold any kind of object!
- A non-generic collection is quite happy to hold anything that is NOT a primitive.
- This meant it was entirely up to the programmer to be…careful.
- Having no way to guarantee collection type wasn’t very programmer-friendly for such a strongly typed language.
And since a collection could hold anything, the methods that get objects out of the collection could have only one kind of return type-java.lang.Object.
That meant that getting a String back out of our only-Strings-intended list required a cast:
String s = (String) myList.get(0);
And since you couldn’t guarantee that what was coming out really was a String (since you were allowed to put anything in the list), the cast could fail at runtime.
So, generics takes care of both ends (the putting in and getting out) by enforcing the type of your collections. Let’s update the String list:
List<String> myList = new ArrayList<String>();
myList.add("Fred"); // OK, it will hold Strings
myList.add(new Dog()); // compiler error!!
Using Generics |
So, now that what you put IN is guaranteed, you can also guarantee what comes OUT, and that means you can get rid of the cast when you get something from the collection.
Instead of
String s = (String)myList.get(0);
// pre-generics, when a String wasn't guaranteed
we can now just say
String s = myList.get(0);
The compiler already knows that myList contains only things that can be assigned to a String reference, so now there’s no need for a cast. So far, it seems pretty simple.
And with the new for loop, you can of course iterate over the guaranteed-to-be-String list:
for (String s : myList) {
int x = s.length();
// no need for a cast before calling a String method!
// The compiler already knew "s" was a String coming from MyList
Generic Methods |
You can write a single generic method declaration that can be called with arguments of different types. Based on the types of the arguments passed to the generic method, the compiler handles each method call appropriately.
Following are the rules to define Generic Methods:
- All generic method declarations have a type parameter section delimited by angle brackets (< and >) that precedes the method’s return type.
- Each type parameter section contains one or more type parameters separated by commas. A type parameter, also known as a type variable, is an identifier that specifies a generic type name.
- The type parameters can be used to declare the return type and act as placeholders for the types of the arguments passed to the generic method, which are known as actual type arguments.
- A generic method’s body is declared like that of any other method. Note that type parameters can represent only reference types not primitive types (like int, double and char).
GenericMethodExample.java
public class GenericMethodExample
{
// generic method printArray
public static < E > void myPintArray( E[] inputArray )
{
// Display array elements
for ( E element : inputArray ){
System.out.printf( "%s ", element );
}
System.out.println();
}
public static void main( String args[] )
{
// Create arrays of Integer, Double and Character
Integer[] intArray = { 1, 2, 3, 4, 5 };
Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };
System.out.println( "Array integerArray contains:" );
myPintArray( intArray ); // pass an Integer array
System.out.println( "\nArray doubleArray contains:" );
myPintArray( doubleArray ); // pass a Double array
System.out.println( "\nArray characterArray contains:" );
myPintArray( charArray ); // pass a Character array
}
}
This would produce following result:
c:\>Javac GenericMethodExample.java
c:\>Java GenericMethodExample
Array integerArray contains:
1 2 3 4 5 6
Array doubleArray contains:
1.1 2.2 3.3 4.4
Array characterArray contains:
H E L L O
Bounded Type Parameters |
- There may be times when you’ll want to restrict the kinds of types that are allowed to be passed to a type parameter.
- For example, a method that operates on numbers might only want to accept instances of Number or its subclasses. This is what bounded type parameters are for.
- To declare a bounded type parameter, list the type parameter’s name, followed by the extends keyword, followed by its upper bound.
MaximumTestExample.java
public class MaximumTestExample
{
// determines the largest of three Comparable objects
public static <T extends Comparable<T> > T myMaximum(T x, T y, T z)
{
T max = x; // assume x is initially the largest
if ( y.compareTo( max ) > 0 ){
max = y; // y is the largest so far
}
if ( z.compareTo( max ) > 0 ){
max = z; // z is the largest now
}
return max; // returns the largest object
}
public static void main( String args[] ) {
System.out.printf( "Max of %d, %d and %d is %d\n\n", 3, 4, 5, myMaximum( 3, 4, 5 ) );
System.out.printf( "Max of %.1f,%.1f and %.1f is %.1f\n\n", 6.6, 8.8, 7.7, myMaximum( 6.6, 8.8, 7.7 ) );
System.out.printf( "Max of %s, %s and %s is %s\n","pear", "apple", "orange", myMaximum( "pear", "apple", "orange" ) );
}}
This would produce following result:
c:\>Javac MaximumTestExample.java
c:\>Java MaximumTestExample
Max of 3, 4 and 5 is 5
Max of 6.6, 8.8 and 7.7 is 8.8
Max of pear, apple and orange is pear
Generic Classes |
- A generic class declaration looks like a non-generic class declaration, except that the class name is followed by a type parameter section.
- As with generic methods, the type parameter section of a generic class can have one or more type parameters separated by commas.
- These classes are known as parameterized classes or parameterized types because they accept one or more parameters.
Box.java
public class Box<T> {
private T t;
public void add(T t) {
this.t = t;
}
public T get() {
return t;
}
public static void main(String[] args) {
Box<Integer> integerBox = new Box<Integer>();
Box<String> stringBox = new Box<String>();
integerBox.add(new Integer(10));
stringBox.add(new String("Hello World"));
System.out.printf("Integer Value :%d\n\n", integerBox.get());
System.out.printf("String Value :%s\n", stringBox.get());
}
}
Output
c:\>javac Box.java
c:\>java Box
Integer Value :10
String Value :Hello World
Recent Comments