GenericsJ8 Home « Generics

In this lesson we unravel the terminology behind generics and look at some of the the strange syntax we get with it. Generics were added to Java in 1.5 and are the biggest of all the changes made to the Java language in the 1.5 release. Although generics are not just for collection types the majority of usage is within collections and so here is a good place to talk about generics.

When we talk about generics or a generic type what we are actually talking about is a parameterized type. So a class, interface or method that has has one or more generic types in its definition can be thought of as generic and we can replace the generic type with a parameterized type. When we define the actual interface, class or method within our code we replace the formal type parameter with the actual type parameter required. As an example we saw in the Collection Hierarchy Diagram that the List interface is specified as List<E>. The following code shows a declaration of an ArrayList<E> using a polymorphic declaration that will only except String objects:


package info.java8;
/*
  In the following code snippet we replace the formal type parameter E
  with an actual type parameter String
*/
List<String> aList = new ArrayList<String>();

So what do generics bring to the party? Even pre 1.5 we could create generalized interfaces, classes or methods by using references of the Object superclass. So you will see a lot of generalized pre 1.5 stuff that works on Object but this approach could not guarantee type safety. The problem being that you need to cast from Object to the actual type being worked on.

For instance every time you wanted to read from a collection you would need to cast every object you read. If an object of the wrong type was inserted into the collection then the cast could cause a runtime exception. The exception might be in a program completely removed from the collection itself and might not happen for a long time, making every collection a possible ticking timebomb. The exception to this were Array objects where we have always implicitly declared the type on array creation, so arrays have always been type safe.

With generic type declarations of collections the compiler knows that only objects of the specified type can be added to this collection or throws up a compiler error. Through this mechanism we can achieve type safety for our collections and not have to worry about someone else inputting an invalid object into our collections.

Because of the huge impact generics had on the language the developers at Sun microsystems, the original writers of Java and still in control when 1.5 was released, made the decision to keep raw types in the language. This decision was made so that the billions of lines of code already written in Java were still compatible in Java. So wherever you see generic code in the language you can, although not in any way recommended, use raw types without any parameterisation instead. Before we get into some coding now is a good time to look at the various terminology used in the rest of this lesson:

Generic Terminology
Term Examples Notes
Raw TypeList
Map
No parameterization.
Generic TypeList<E>
Map<K,V>
An interface, class or method followed by one or more formal type parameters within angled brackets.
Formal Type parameterE
K,V
One or more formal type parameters delimitied by commas, which can be thought of as placeholders for the actual type parameters.
Parameterized TypeList<String>
Map<aKey,aValue>
An interface, class or method followed by one or more actual type parameters within angled brackets.
Actual Type parameterString
aKey,aValue
One or more actual type parameters delimitied by commas. Object types are allowed when using generics but primitive types are not.
Bounded Type<T extends Number>Create a superclass boundary which all actual type parameters must be or extend from.
Unbounded Wildcard TypesList<?>Unknown type.
Bounded Wildcard TypesList<? extends Number>Create a superclass boundary which all unknown types must be or extend from.

Creating A Folder For Our Collections/Generics Source Files

As we are about to write our first code of a new section we will create a folder for our Collections/Generics files, in Windows this would be:

double click My Computer icon

double click C:\ drive (or whatever your local drive is)

right click the mouse in the window

from the drop down menu Select New

click the Folder option and call the folder _Collections and press enter.

Raw Type/Generic Type Comparison Top

Lets take a look at some non-generic code and the pitfalls associated with type safety and then use some type safe generic code to do the same thing.

Raw Type Example

First we will create a raw type collection where we only want String objects stored and add an Integer object:


package info.java8;
/*
  Test class for Raw Type Collection
*/
import java.util.*; // Import the java.util package

public class RawTypeNameCollection {
    /*
     Raw Type name ArrayList containing only String instances
    */ 
    private static final Collection names = new ArrayList();

    public static void main (String[] args) {
        // Add to our collection
        names.add(new String("Bill"));
        names.add(new String("Ben"));

        // Nothing to stop us adding an Integer
        names.add(new Integer(6));

        // Now use a raw iterator to iterate over our collection
        for (Iterator i = names.iterator(); i.hasNext(); ) {
            String aName = (String) i.next(); // ClassCastException (on Integer)
            System.out.println(aName);  // Print name
        }
    }
}

Save and compile the RawTypeNameCollection class in directory   c:\_Collections in the usual way.

Compile raw type 1

As you can see the program compiles but the compiler gives us a couple of messages which we ignore at our peril. So recompile the program as suggested with the -Xlint:unchecked option:

Compile raw type 2

Compiling with the -Xlint:unchecked option gives us a lot more information contained within the compiler warnings. The compiler is basically telling us we are using non generic code for our polymorphic Collection and hence is not type safe. So the compiler has done its job and told us we are using a raw type, but the code is still executable because legacy code is still allowed. Lets run the code:

Run raw type

As expected we get a ClassCastException when we iterate over the Integer object. You can imagine a scenario where our collection is public and accessed elsewhere and the effort involved to track down the erroneous code.

Generic Type Example

Now we will create a generic type collection where we only want String objects stored and add an Integer object:


package info.java8;
/*
  Test class for Generic Type Collection
*/
import java.util.*; // Import the java.util package

public class GenericTypeNameCollection {
    /*
     Generic Type name ArrayList containing only String instances
    */ 
    private static final Collection<String> names = new ArrayList<String>();

    public static void main (String[] args) {
        // Add to our collection
        names.add(new String("Bill"));
        names.add(new String("Ben"));

        // Now try adding an Integer
        names.add(new Integer(6));

        // Now use a parameterised type iterator to iterate over our collection
        for (Iterator<String> i = names.iterator(); i.hasNext(); ) {
            String aName = i.next(); // No cast required
            System.out.println(aName);  // Print name
        }

        // Use enhanced for loop to iterate over collection (preferred)
        for (String s : names) { // No cast required
            System.out.println(s);  // Print name
        }
    }
}

Save and compile the GenericTypeNameCollection class in directory   c:\_Collections in the usual way.

Compile generic type

The compile fails when we try to add an Integer to our type safe ArrayList<String> which is exactly what we want. There is now no chance of getting a ClassCastException at runtime as the code won't even compile. If our collection was public and someone tried to put in anything else other than String objects they won't even get a clean compile. Remove the code that tries to insert the Integer object and recompile and run the class in the normal way.

Run generic type

We iterate over the loop twice, the first time with a parameterised type iterator and the second time using the enhanced for loop introduced in Java. In neither case do we need to cast as the compiler has already inserted casts for us when compiling the code. Also using the enhanced for loop is much easier to write, more elegant and recommended when iterating over collections.

Generic Interfaces Top

With generics we can create our own generic interfaces which allow us to create generalized interfaces with all the type safety that generics gives us.

Following is a simple generic interface and class so you can see how we can create a generic interface and implement it. We are using T as the placeholder for our formal type parameter in this instance.; which you can think of as a pneumonic for Type. In the collections example above we used the E pneumonic for Element. When creating your own generic types it is best parctice to use pneumonics for the formal type parameter rather than multiple letters.

In the overridden showObjectType() method we are using the getClass() method of Object to get the actual Class type which we chain to getName(), which is a method of the Class object, which can be found in the java.lang package and print this to the console. Notice how we use the T formal type parameter within the declaration and constructor.


package info.java8;
/*
  Simple generic interface that accepts any object
*/
interface SimpleGenericInterface<T> { 

    // Implementation will show object type
    void showObjectType();
}


/*
  Simple generic class that implements SimpleGenericInterface
*/
public class SimpleGenericInterfaceImpl<T> implements SimpleGenericInterface<T> {

    // Generic object declaration
    T genericObj;

    // Pass reference to object of type T to our constructor
    SimpleGenericInterfaceImpl(T genericObj) {
        this.genericObj = genericObj;
    }

    // Output object type of T to console
    public void showObjectType() {
        System.out.println("Object type of T is " + genericObj.getClass().getName());
    }
}

Save and compile the SimpleGenericInterface interface and SimpleGenericInterfaceImpl class in directory   c:\_Collections in the usual way.

Compile generic class

Now we need to write a test class to show how we can pass various objects to our SimpleGenericInterfaceImpl class:


package info.java8;
/*
  Test our SimpleGenericClass class that accepts any object
*/
public class TestSimpleGenericInterfaceImpl {

    public static void main(String args[]) {
        // Test the SimpleGenericInterfaceImpl using a String object
        SimpleGenericInterfaceImpl<String> genStringObj = new SimpleGenericInterfaceImpl<String>("ab");
        genStringObj.showObjectType();
        // Test the SimpleGenericInterfaceImpl using an Integer object
        SimpleGenericInterfaceImpl<Integer> genIntegerObj = new SimpleGenericInterfaceImpl<Integer>(1);
        genIntegerObj.showObjectType();
        // Test the SimpleGenericInterfaceImpl using a Double object
        SimpleGenericInterfaceImpl<Double> genDoubleObj = new SimpleGenericInterfaceImpl<Double>(1.2);
        genDoubleObj.showObjectType();
    }
}

Save, compile and run the TestSimpleGenericInterfaceImpl class in directory   c:\_Collections in the usual way.

Run generic class

So from the screenshot you can see that our TestSimpleGenericClass class works with the various objects passed to it replacing the T formal type parameter placeholder with the actual type parameter of the object passed. We mentioned ealier that the compiler inserts the casts required to make generics work when we run our bytecode. What we haven't mentioned yet is that generic code is implemented by erasure, which means that generic code enforces type constraints only at compile time. The element type information is erased in the runtime bytecode, which is how legacy code can freely interoperate with generic code. So the point here is that at runtime there is only one version of the SimpleGenericInterfaceImpl class that works through the casts inserted automatically by the compiler into the runnable bytecode.

Generally if a class implements a generic interface then that class must be generic as well, so that it takes a type parameter that is passed to the interface. If an actual parameter is used when implementing the interface, then the class doesn't have to be generic. The following code snippet should clarify this:


package info.java8;
// Following is correct as used above
public class SimpleGenericInterfaceImpl<T> implements SimpleGenericInterface<T> { 

// Following is incorrect as no type specifed for class and formal type parameter specified
public class SimpleGenericInterfaceImpl implements SimpleGenericInterface<T> { 

// Following is fine as actual type specified for implementation, so class is not generic
public class SimpleGenericInterfaceImpl implements SimpleGenericInterface<String> { 

Generic Classes Top

With generics we also have the option to create our own generic classes which allow us to create generalized classes in much the same way as using an Object reference but with all the type safety that generics gives us.

Following is a simple generic class so you can see how we can create a generic class. We are using T as the placeholder for our formal type parameter in this instance.

We are using the getClass() method of Object to get the actual Class type which we chain to getName(), which is a method of the Class object, which can be found in the java.lang package and print this to the console. Notice how we use the T formal type parameter within the declaration, constructor and getGenericObj() getter method.


package info.java8;
/*
  Simple generic class that accepts any object
*/
public class SimpleGenericClass<T> {

    // Generic object declaration
    T genericObj;

    // Pass reference to object of type T to our constructor
    SimpleGenericClass(T genericObj) {
        this.genericObj = genericObj;
    }

    // Output object type of T to console
    void showObjectType() {
        System.out.println("Object type of T is " + genericObj.getClass().getName());
    }
    
    // Return the generic object
    T getGenericObj() {
        return genericObj;
    }
}

Save and compile the SimpleGenericClass class in directory   c:\_Collections in the usual way.

Compile generic class

Now we need to write a test class to show how we can pass various objects to our SimpleGenericClass class:


package info.java8;
/*
  Test our SimpleGenericClass class that accepts any object
*/
public class TestSimpleGenericClass {

    public static void main(String args[]) {
        // Test the SimpleGenericClass using a String object
        SimpleGenericClass<String> genStringObj = new SimpleGenericClass<String>("A stitch in time");
        genStringObj.showObjectType();
        String str = genStringObj.getGenericObj();
        System.out.println("String value is: " + str);
        // Test the SimpleGenericClass using an Integer object
        SimpleGenericClass<Integer> genIntegerObj = new SimpleGenericClass<Integer>(123);
        genIntegerObj.showObjectType();
        int i = genIntegerObj.getGenericObj();
        System.out.println("Integer value is: " + i);
        // Test the SimpleGenericClass using a Double object
        SimpleGenericClass<double> genDoubleObj = new SimpleGenericClass<double>(123.456);
        genDoubleObj.showObjectType();
        double d = genDoubleObj.getGenericObj();
        System.out.println("Double value is: " + d);
    }
}

Save and compile the TestSimpleGenericClass class in directory   c:\_Collections in the usual way.

Compile test generic class

As you can see from the screenshot we got an error. As mentioned in our terminology table above you can't use primitive types with generics. So correct the code by changing the double generic type and actual parameter type to Double for the genDoubleObj instantiation.

Save, compile and run the TestSimpleGenericClass class in directory   c:\_Collections in the usual way.

Run generic class

So from the screenshot you can see that our TestSimpleGenericClass class works with the various objects passed to it replacing the T formal type parameter placeholder with the actual type parameter of the object passed.

Bounded Types Top

Generics are by design invariant, which means for any two distinct types Type1 and Type2, List<Type1> is neither a supertype or subtype of List<Type2>. The following code snippet should help with understanding of this statement:


package info.java8;
/*
  Generics are invariant
*/
List<Object> aList = new ArrayList<String>(); // Invalid
List<String> aList = new ArrayList<String>(); // Valid

You would think that because String like all objects extends Object that the first statement is prefectly valid, but generics doesn't work like polymorphic types because of invariance. So the simple rule here is that the reference type must always match the actual object type when using generics.

But say for instance we want to create a generic class that uses subclasses of the Number class to do some mathematical calculations. How do we get the different types to work? First lets look at generic invariance by writing a class:


package info.java8;
/*
  Generic type invariance
*/
public class BoundedTypeClass<T> {

    // Generic object declaration
    T genericNumberObj;

    // Pass reference to object of type T to our constructor
    BoundedTypeClass(T genericNumberObj) {
        this.genericNumberObj = genericNumberObj;
    }

    // Return the square of the integer
    double squareInt() {
        return genericNumberObj.intValue()  * genericNumberObj.intValue();
    }

    // Return the square of the fraction
    double squareDbl() {
        return genericNumberObj.doubleValue()  * genericNumberObj.doubleValue();
    }
}

Save and compile the BoundedTypeClass class in directory   c:\_Collections in the usual way.

Compile bounded class 1

The compiler has no way of knowing we are only passing numeric types and we want to use subclasses of Number. Luckily for us Java generics come with bounded types which allows us to specify a superclass boundary which our formal type parameters must be or extend from. So in our case we need to change our generic type from <T> to <T extends Number>. This is now telling the compiler that our formal type parameter of <T> must either be a Number or a subclass thereof. Lets modify our class declaration to do this:


package info.java8;
/*
  Generic type invariance
*/
public class BoundedTypeClass<T extends Number> {  // Bounded Type

    // Generic object declaration
    T genericNumberObj;

    // Pass reference to object of type T to our constructor
    BoundedTypeClass(T genericNumberObj) {
        this.genericNumberObj = genericNumberObj;
    }

    // Return the square of the integer
    double squareInt() {
        return genericNumberObj.intValue()  * genericNumberObj.intValue();
    }

    // Return the square of the fraction
    double squareDbl() {
        return genericNumberObj.doubleValue()  * genericNumberObj.doubleValue();
    }
}

Save and compile the BoundedTypeClass class in directory   c:\_Collections in the usual way.

Compile bounded class 2

The program compiles fine now and will only allow subclasses of the Number to use our class. We need to write a test class to show how we can pass various subclasses of Number to our BoundedTypeClass class and nothing else:


package info.java8;
/*
  Test our BoundedTypeClass class that only accepts Number subclasses
*/
public class TestBoundedTypeClass {

    public static void main(String args[]) {
        // Test the BoundedTypeClass using an Integer object
        BoundedTypeClass<Integer> genIntegerObj = new BoundedTypeClass<Integer>(12);
        System.out.println("Square of genIntegerObj is: " + genIntegerObj.squareInt());
        System.out.println("Fractional Square of genIntegerObj is: " + genIntegerObj.squareDbl());
        // Test the BoundedTypeClass using a Double object
        BoundedTypeClass<Double> genDoubleObj = new BoundedTypeClass<Double>(12.12);
        System.out.println("Square of genDoubleObj is: " + genDoubleObj.squareInt());
        System.out.println("Fractional Square of genDoubleObj is: " + genDoubleObj.squareDbl());
        // Test the BoundedTypeClass using a String object
        BoundedTypeClass<String> genStringObj = new BoundedTypeClass<String>("A stitch in time");
        System.out.println("Square of genStringObj is: " + genStringObj.squareInt());
        System.out.println("Fractional Square of genStringObj is: " + genStringObj.squareDbl());
    }
}

Save and compile the TestBoundedTypeClass class in directory   c:\_Collections in the usual way.

Compile test bounded class

As you can see from the screenshot we got a compiler error for the String object as this doesn't extend Number which is what we would expect. So remove the comment and last three statements for the String invocation of the BoundedTypeClass class from the main() method.

Save, compile and run the TestBoundedTypeClass class in directory   c:\_Collections in the usual way.

Run test bounded class

So from the screenshot you can see that our BoundedTypeClass class works with subclasses of the Number object due to our use of bounded types.

Unbounded Wildcard Types Top

We saw how we can use unbounded types to get around the problem with invariance above. Lets extend our BoundedTypeClass class to compare the actual integer values of objects passed in to see if they are equal.


package info.java8;
/*
  Generic type invariance
*/
public class BoundedTypeClass<T extends Number> {  // Bounded Type

    // Generic object declaration
    T genericNumberObj;

    // Pass reference to object of type T to our constructor
    BoundedTypeClass(T genericNumberObj) {
        this.genericNumberObj = genericNumberObj;
    }

    // Return the square of the integer
    double squareInt() {
        return genericNumberObj.intValue()  * genericNumberObj.intValue();
    }

    // Return the square of the fraction
    double squareDbl() {
        return genericNumberObj.doubleValue()  * genericNumberObj.doubleValue();
    }

    // Return true if integer values of two objects are equal
    boolean intEqual(BoundedTypeClass<T> obj) { // Input T type
        if (genericNumberObj.intValue() == obj.genericNumberObj.intValue()) {
            return true;
        } else {
            return false;
        }
    }
}

Save and compile the BoundedTypeClass class in directory   c:\_Collections in the usual way.

Compile bounded class 2

The program compiles fine, so now we need to write a test class to see if everything still works and we can compare integer values:


package info.java8;
/*
  Test our BoundedTypeClass class that only accepts Number subclasses
*/
public class TestBoundedTypeClass {

    public static void main(String args[]) {
        // Test the BoundedTypeClass using an Integer object
        BoundedTypeClass<Integer> genIntegerObj = new BoundedTypeClass<Integer>(12);
        System.out.println("Square of genIntegerObj is: " + genIntegerObj.squareInt());
        System.out.println("Fractional Square of genIntegerObj is: " + genIntegerObj.squareDbl());
        // Test the BoundedTypeClass using another Integer object
        BoundedTypeClass<Integer> genIntegerObj2 = new BoundedTypeClass<Integer>(13);
        System.out.println("Square of genIntegerObj2 is: " + genIntegerObj2.squareInt());
        System.out.println("Fractional Square of genIntegerObj2 is: " + genIntegerObj2.squareDbl());
        // Test the BoundedTypeClass using a Double object
        BoundedTypeClass<Double> genDoubleObj = new BoundedTypeClass<Double>(12.12);
        System.out.println("Square of genDoubleObj is: " + genDoubleObj.squareInt());
        System.out.println("Fractional Square of genDoubleObj is: " + genDoubleObj.squareDbl());
        // Test the BoundedTypeClass using another Double object
        BoundedTypeClass<Double> genDoubleObj2 = new BoundedTypeClass<Double>(13.13);
        System.out.println("Square of genDoubleObj2 is: " + genDoubleObj2.squareInt());
        System.out.println("Fractional Square of genDoubleObj2 is: " + genDoubleObj2.squareDbl());
        // Test integer equality of objects
        if (genIntegerObj.intEqual(genIntegerObj2)) {
            System.out.println("Integer values are equal");
        } else {
            System.out.println("Integer values are not equal");
        }
        if (genDoubleObj.intEqual(genDoubleObj2)) {
            System.out.println("Integer values are equal");
        } else {
            System.out.println("Integer values are not equal");
        }
        if (genIntegerObj.intEqual(genDoubleObj)) {
            System.out.println("Integer values are equal");
        } else {
            System.out.println("Integer values are not equal");
        }
    }
}

Save and compile the TestBoundedTypeClass class in directory   c:\_Collections in the usual way.

Compile test bounded class 2

As you can see from the screenshot we got a compiler error for the call to the intEqual() method. In the first two calls to the intEqual() method the invoking and parameter object are the same and so there is no problem. In the last call we have an invoking object of Integer and a parameter object of Double and as the compiler error points out we cant convert the latter bounded class type to the former. We need a way to let the compiler know that the input parameter, which we know can only be instantiated as a Number object or subclass thereof is an unknown type. We can achieve this by using an unbounded wildcard type, which is specified using the ? symbol, as the input parameter to the intEqual() method of the BoundedTypeClass class.

Change the signature to boolean intEqual(BoundedTypeClass<?> obj) and recompile the BoundedTypeClass class.

Save,compile and run the TestBoundedTypeClass class in directory   c:\_Collections in the usual way.

Run test bounded class 2

The BoundedTypeClass class now works as shown in the screenshot through our use of an unbounded wildcard type.

Bounded Wildcard Types Top

We saw how we can use unbounded types to get around the problem with invariance and we then looked at how to allow an unknown type by using unbounded wildcard types. What if we had a scenario where we only want a method to work on a particular clas or subclasses of it? For this type of situation we can use bounded wildcard types to only allow certain classes to use a method. Ok, lets start by creating and compiling some simple classes:


package info.java8;
public class A { 
    {
    System.out.println("A Object initializer"); 
    }
}

public class B extends A { 
    {
    System.out.println("B Object initializer"); 
    }
}

public class C extends B {    
    {
    System.out.println("C Object initializer"); 
    }
}  

public class D { // Nothing to do  with other classes    
    {
    System.out.println("D Object initializer"); 
    }
}

Save and compile the A, B, C and D classes in directory   c:\_Collections in the usual way.

Compile generic class

Now we need to write a simple generic class that will take any type and haev a method within in that will eventually only work on the A class and subclasses of it; but first we will let the method work with any type:


package info.java8;
/*
  Simple generic class that accepts any object
*/
public class BoundedWildcardType<T> {

    // Generic object declaration
    T genericObj;

    // Pass reference to object of type T to our constructor
    BoundedWildcardType(T genericObj) {
        this.genericObj = genericObj;
    }

    // Output object type of BoundedWildcardType to console 
    void showObjectType(BoundedWildcardType obj) {
        System.out.println("Object type of BoundedWildcardType is " + genericObj.getClass().getName());
    }
}

Save and compile the BoundedWildcardType class in directory   c:\_Collections in the usual way.

Compile bounded wilcard class

Now we need to write a test class to show how we can pass various objects to our BoundedWildcardType class:


package info.java8;
/*
  Test our BoundedWildcardType class that accepts any object
*/
public class TestBoundedWildcardType {

    public static void main(String args[]) { 
        // Create some objects
        A a = new A(); 
        B b = new B(); 
        C c = new C(); 
        D d = new D();  
        // Test the BoundedWildcardType using A, B, C and D objects
        BoundedWildcardType<A> genAObj = new BoundedWildcardType<A>(a);
        genAObj.showObjectType(genAObj);
        BoundedWildcardType<B> genBObj = new BoundedWildcardType<B>(b);
        genBObj.showObjectType(genBObj);
        BoundedWildcardType<C> genCObj = new BoundedWildcardType<C>(c);
        genCObj.showObjectType(genCObj);
        BoundedWildcardType<D> genDObj = new BoundedWildcardType<D>(d);
        genDObj.showObjectType(genDObj);
    }
}

Save, compile and run the TestBoundedWildcardType class in directory   c:\_Collections in the usual way.

Run bounded wilcard class 1

Well nothing new here, all the classes run as expected. Now we want to change the BoundedWildcardType class so that only objects of type A or sublasses thereof can use the showObjectType(BoundedWildcardType obj) method and for this we need a bounded wildcard type.

Change the signature to showObjectType(BoundedWildcardType<? extends A> obj) and recompile the BoundedTypeClass class.

Save and compile the TestBoundedTypeClass class in directory   c:\_Collections in the usual way.

Compile test bounded wilcard class

As you can see from the screenshot above the compiler won't allow us to call the showObjectType(BoundedWildcardType<? extends A> obj) method with an object of class D because the method now has an unbounded wildcard type that only allows the A class and its subclasses.

Remove the creation and call for the genDObj in the TestBoundedTypeClass class and save,compile and run the class in directory   c:\_Collections in the usual way.

Run bounded wilcard class 2

The BoundedWildcardType class now works as shown in the screenshot through our use of an unbounded wildcard type.

Generic Methods Top

We have seen several examples of generic methods so far in this lesson and these were all contained within genric classes. It is also possible to have generic methods inside non-generic classes.

Following is a non-generic class that contains a generic method. Two things of note are generic methods can be static or non-static and notice where we declare the bounded type. When declaring a generic method in a non-generic class then we have to declare the generic type before the return type within the method:


package info.java8;
/*
  Non-generic class containing a generic method
*/
public class SquareNumber{

    public static void main (String[] args) {
        Double d = new Double(12.12);
        genMethod(d);
        Integer i = new Integer(11);
        genMethod(i);
        String s = new String("generics");
        genMethod(s);
    }
	
    /*
      Method to print square of integer
    */ 
    static <T extends Number> void genMethod (T number) {
        System.out.println("Input squared as integer = " + number.intValue() * number.intValue());
    }
}

Save and compile the SquareNumber class in directory   c:\_Collections in the usual way.

Compile generic method

The compile files which it should do as the String object does not extend number. So remove the last 2 lines of code from the main() and save, recompile and run the SquareNumber class.

Run generic method

As the screenshot shows the method now works fine and will only except Number objects and its subclasses.

Generic Constructors Top

We have seen several examples of generic constructors so far in this lesson and these were all contained within genric classes. It is also possible to have generic constructors inside non-generic classes.

Following is a non-generic class that contains a generic constructor. Notice where we declare the bounded type. When declaring a generic constructor in a non-generic class then we have to declare the generic type before the constructor name:


package info.java8;
/*
  Non-generic class containing a generic constructor
*/
class CubeNumber { 
    private int cubedNumber;
    
    // Pass bounded type to our constructor
    <T extends Number> CubeNumber(T number) {
        cubedNumber = number.intValue() * number.intValue() * number.intValue(); 
    }

    // getter for cubed number
    public int getNumber() {
        return cubedNumber;
    }
}

Save and compile the CubeNumber class in directory   c:\_Collections in the usual way.

Compile generic constructor

Now we need to write a test class to show how we can pass various Number objects to our CubeNumber class:


package info.java8;
/*
  Test our CubeNumber class that accepts Number and subclasses for its constructor
*/
public class TestCubeNumber {

    public static void main(String args[]) {
        // Test the CubeNumber using an Integer object
        CubeNumber integerObj = new CubeNumber(12);
        System.out.println("Input cubed = " + integerObj.getNumber());
        // Test the CubeNumber using a Double object
        CubeNumber doubleObj = new CubeNumber(13.45);
        System.out.println("Input cubed = " + doubleObj.getNumber());
    }
}

Save, compile and run the TestCubeNumber class in directory   c:\_Collections in the usual way.

Run generic class

So from the screenshot you can see that our TestSimpleGenericClass class works with the various objects passed to the constructor replacing the T formal type parameter placeholder with the actual type parameter of the object passed.

Lesson 2 Complete

In this lesson we looked at generics and how to use them.


What's Next?

In the next lesson we look at Sets, as these are probably the easiest collection type to understand and as such a good starting point.

<