Categories
  Java Applet
  Java for beginners
Lesson: Generics
By Admin
 
Share
 

Generics are a built-in language feature that will make your software more reliable. This lesson discusses the following topics:

 

Generic Types

Let's update our Box class to use generics. We'll first create a generic type declaration by changing the code "public class Box" to "public class Box"; this introduces one type variable, named T, that can be used anywhere inside the class. This same technique can be applied to interfaces as well. There's nothing particularly complex about this concept. In fact, it's quite similar to what you already know about variables in general. Just think of T as a special kind of variable, whose "value" will be whatever type you pass in; this can be any class type, any interface type, or even another type variable. It just can't be any of the primitive data types. In this context, we also say that T is a formal type parameter of the Box class.

/**
 * Generic version of the Box class. 
 */
public class Box {
 
    private T t; // T stands for "Type"          
 
    public void add(T t) {
        this.t = t;
    }
 
    public T get() {
        return t;
    }
}

As you can see, we've replaced all occurrences of Object with T. To reference this generic class from within your own code, you must perform a generic type invocation, which replaces T with some concrete value, such as Integer:

Box integerBox;

You can think of a generic type invocation as being similar to an ordinary method invocation, but instead of passing an argument to a method, you're passing a type argumentInteger in this case — to the Box class itself. Like any other variable declaration, this code does not actually create a new Box object. It simply declares that integerBox will hold a reference to a "Box of Integer", which is how Box is read.

An invocation of a generic type is generally known as a parameterized type.

To instantiate this class, use the new keyword, as usual, but place between the class name and the parenthesis:

integerBox = new Box();

Or, you can put the entire statement on one line, such as:

Box integerBox = new Box();

Once integerBox is initialized, you're free to invoke its get method without providing a cast, as in BoxDemo3:

 
public class BoxDemo3 {
 
    public static void main(String[] args) {
        Box integerBox = new Box();
        integerBox.add(new Integer(10));
        Integer someInteger = integerBox.get(); // no cast!
        System.out.println(someInteger);
    }
}

Furthermore, if you try adding an incompatible type to the box, such as String, compilation will fail, alerting you to what previously would have been a runtime bug:

    BoxDemo3.java:5: add(java.lang.Integer) in Box    
    cannot be applied to (java.lang.String)
        integerBox.add("10");
                  ^
    1 error

It's important to understand that type variables are not actually types themselves. In the above examples, you won't find T.java or T.class anywhere on the filesystem. Furthermore, T is not a part of the Box class name. In fact during compilation, all generic information will be removed entirely, leaving only Box.class on the filesystem. We'll discuss this later in the section on Type Erasure

Also note that a generic type may have multiple type parameters, but each parameter must be unique within its declaring class or interface. A declaration of Box, for example, would generate an error on the second occurrence of T, but Box, however, would be allowed.

Type Parameter Naming Conventions

By convention, type parameter names are single, uppercase letters. This stands in sharp contrast to the variable naming conventions that you already know about, and with good reason: Without this convention, it would be difficult to tell the difference between a type variable and an ordinary class or interface name.

The most commonly used type parameter names are:

·         E - Element (used extensively by the Java Collections Framework)

·         K - Key

·         N - Number

·         T - Type

·         V - Value

·         S,U,V etc. - 2nd, 3rd, 4th types

You'll see these names used throughout the Java SE API and the rest of this tutorial.

 

Source - http://java.sun.com/docs/books/tutorial/java/generics/gentypes.html

 

 

Generic Methods and Constructors

 

Type parameters can also be declared within method and constructor signatures to create generic methods and generic constructors. This is similar to declaring a generic type, but the type parameter's scope is limited to the method or constructor in which it's declared.

/**
 * This version introduces a generic method.
 */
public class Box {
 
    private T t;          
 
    public void add(T t) {
        this.t = t;
    }
 
    public T get() {
        return t;
    }
 
    public  void inspect(U u){
        System.out.println("T: " + t.getClass().getName());
        System.out.println("U: " + u.getClass().getName());
    }
 
    public static void main(String[] args) {
        Box integerBox = new Box();
        integerBox.add(new Integer(10));
        integerBox.inspect("some text");
    }
}

Here we've added one generic method, named inspect, that defines one type parameter, named U. This method accepts an object and prints its type to standard output. For comparison, it also prints out the type of T. For convenience, this class now also has a main method so that it can be run as an application.

The output from this program is:

T: java.lang.Integer
U: java.lang.String

By passing in different types, the output will change accordingly.

A more realistic use of generic methods might be something like the following, which defines a static method that stuffs references to a single item into multiple boxes:

public static  void fillBoxes(U u, List> boxes) {
    for (Box box : boxes) {
        box.add(u);
    }
}

To use this method, your code would look something like the following:

Crayon red = ...;
List> crayonBoxes = ...;

The complete syntax for invoking this method is:

Box.fillBoxes(red, crayonBoxes);

Here we've explicitly provided the type to be used as U, but more often than not, this can be left out and the compiler will infer the type that's needed:

Box.fillBoxes(red, crayonBoxes); // compiler infers that U is Crayon

This feature, known as type inference, allows you to invoke a generic method as you would an ordinary method, without specifying a type between angle brackets.

 

Source - http://java.sun.com/docs/books/tutorial/java/generics/genmethods.html

 

 

 

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, which in this example is Number. Note that, in this context, extends is used in a general sense to mean either "extends" (as in classes) or "implements" (as in interfaces).

/**
 * This version introduces a bounded type parameter.
 */
public class Box {
 
    private T t;          
 
    public void add(T t) {
        this.t = t;
    }
 
    public T get() {
        return t;
    }
 
    public extends Number> void inspect(U u){
        System.out.println("T: " + t.getClass().getName());
        System.out.println("U: " + u.getClass().getName());
    }
 
    public static void main(String[] args) {
        Box integerBox = new Box();
        integerBox.add(new Integer(10));
        integerBox.inspect("some text"); // error: this is still String! 
    }
}

By modifying our generic method to include this bounded type parameter, compilation will now fail, since our invocation of inspect still includes a String:

Box.java:21: inspect(U) in Box cannot
  be applied to (java.lang.String)
                        integerBox.inspect("10");
                                  ^
1 error

To specify additional interfaces that must be implemented, use the & character, as in:

 

 

Source - http://java.sun.com/docs/books/tutorial/java/generics/bounded.html

 

 

Subtyping

As you already know, it's possible to assign an object of one type to an object of another type provided that the types are compatible. For example, you can assign an Integer to an Object, since Object is one of Integer's supertypes:

    Object someObject = new Object();
    Integer someInteger = new Integer(10);
    someObject = someInteger; // OK

In object-oriented terminology, this is called an "is a" relationship. Since an Integer is a kind of Object, the assignment is allowed. But Integer is also a kind of Number, so the following code is valid as well:

    public void someMethod(Number n){
        // method body omitted 
    }
 
    someMethod(new Integer(10)); // OK
    someMethod(new Double(10.1)); // OK

The same is also true with generics. You can perform a generic type invocation, passing Number as its type argument, and any subsequent invocation of add will be allowed if the argument is compatible with Number:

    Box box = new Box();
    box.add(new Integer(10)); // OK
    box.add(new Double(10.1)); // OK

Now consider the following method:

    public void boxTest(Box n){
        // method body omitted 
    }

What type of argument does it accept? By looking at its signature, we can see that it accepts a single argument whose type is Box. But what exactly does that mean? Are you allowed to pass in Box or Box, as you might expect? Surprisingly, the answer is "no", because Box and Box are not subtypes of Box.

Understanding why becomes much easier if you think of tangible objects — things you can actually picture — such as a cage:

        // A cage is a collection of things, with bars to keep them in.
        interface Cage extends Collection;

Note: The Collection interface is the root interface of the collection hierarchy; it represents a group of objects. Since a cage would be used for holding a collection of objects (the animals), it makes sense to include it in this example.


A lion is a kind of animal, so Lion would be a subtype of Animal:

        interface Lion extends Animal {}
        Lion king = ...;

Where we need some animal, we're free to provide a lion:

        Animal a = king;

A lion can of course be put into a lion cage:

        Cage lionCage = ...;
        lionCage.add(king);

and a butterfly into a butterfly cage:

        interface Butterfly extends Animal {}
        Butterfly monarch = ...;
        Cage butterflyCage = ...;
        butterflyCage.add(monarch);

But what about an "animal cage"? English is ambiguous, so to be precise let's assume we're talking about an "all-animal cage":

        Cage animalCage = ...;

This is a cage designed to hold all kinds of animals, mixed together. It must have bars strong enough to hold in the lions, and spaced closely enough to hold in the butterflies. Such a cage might not even be feasible to build, but if it is, then:

        animalCage.add(king);
        animalCage.add(monarch);

Since a lion is a kind of animal (Lion is a subtype of Animal), the question then becomes, "Is a lion cage a kind of animal cage? Is Cage a subtype of Cage?". By the above definition of animal cage, the answer must be "no". This is surprising! But it makes perfect sense when you think about it: A lion cage cannot be assumed to keep in butterflies, and a butterfly cage cannot be assumed to hold in lions. Therefore, neither cage can be considered an "all-animal" cage:

        animalCage = lionCage; // compile-time error
        animalCage = butterflyCage; // compile-time error
 

Without generics, the animals could be placed into the wrong kinds of cages, where it would be possible for them to escape.