Showing posts with label Java Generics. Show all posts
Showing posts with label Java Generics. Show all posts

Sunday, January 1, 2012

Type Wildcard in Java Generics - A Tutorial

Overview

Type wildcard is the most tricky part of Java Generics. The readers are assumed having the basic knowledge of Java Generics. For a quick review of the basics of Java Generics, see my other post, The Basics of Java Generics. Type wildcard is only used in parameterized types, as the type arguments. A type wildcard is never used in a definition of generic type or generic method . In practice, such parameterized types are commonly used in method definitions, serving as the type of the formal parameters of the methods.

A parameterized type may take one of four forms:
  1. List<String>, here String is a concrete type, serving as the type argument for the parameterized type List<String>
  2. List<?>, where ? is a type wildcard without a bound, serving as the type argument for the parameterized type List<?> 
  3. List<? extends Number>, where ? is a type wildcard with a upper bound, serving as the type argument for the parameterized type List<? extends Number>, and Number is the upper bound of the wildcard.
  4. List<? supper Number>, where ? is a type wildcard with a lower bound, serving as the type argument for the parameterized type List<? super Number>, and Number is the lower bound of the wildcard.
For a parameterized type with a type wildcard as a type argument, the wildcard may have either a single upper or lower bound, but not both.

In a World without Type Wildcard

To understand why Java introduces the type wildcard for parameterized type, let’s look at an example. This example is about a circus. In the circus, there are two kinds of animals, birds and dogs. Brids can fly. Dogs can bark. A special kind of birds, nightingares, can also sing. Class hierarchy of animals used in this example are shown in Figure 1. Code for all kinds of animals are shown in Listing 1-1 to 1-4.

Figure 1 - Animal Hierarchy


Listing 1-1 - Animal
package animals;

public class Animal {
    private String name;
    
    public Animal(String name) {
        this.name = name;
    }
    
    public void jump() {
        System.out.println(getName() + " is jumping.");
    }
    
    public String getName() {
        return name;
    }
}

Listing 1-2 -Brid
package animals;

public class Bird extends Animal {    
    public Bird(String name) {
        super(name);
    }
    
    public void fly() {
        System.out.println(getName() + " is flying.");
    }
}

Listing 1-3 -  Nightingale
package animals;

public class Nightingale extends Bird {
    public Nightingale(String name) {
        super(name);
    }
    
    public void sing() {
        System.out.println(getName() + " is singing.");
    }
}

Listing 1-4 - Dog
package animals;

public class Dog extends Animal {   
    public Dog(String name) {
        super(name);
    }
    
    public void bark() {
        System.out.println(getName() + " is barking.");
    }
}

There is a kind of actors in the circus called AnimalTrainer. When given a collection of animals, the AnimalTrainer commands them to jump. The first version of AnimalTrainer is shown in Listing 2-1.

Listing 2-1 - AnimalTrainer Version 1 - Without Type Wildcard
package nowildcard;
import java.util.List;
import animals.Animal;

public class AnimalTrainer {
    public void act(List<Animal> animalList) {
        for (Animal animal : animalList) {
            animal.jump();
        }
    }
}

The first version of the main class, Circus, is shown in Listing 2-2. When the Circus program is launched, it will at first create an AnimalTrainer. Then a list of Animals is created, and two animals are added into the list. It finally invokes the act method, passing the animal list, on the AnimalTrainer. The AnimalTrainer acts by command every animal to jump. So far so good.

Listing 2-2 - Circus Version 1
package nowildcard;
import java.util.ArrayList;
import java.util.List;
import animals.Animal;
import animals.Bird;
import animals.Dog;

public class Circus {
    public static void main(String[] args) {
        AnimalTrainer animalTrainer = new AnimalTrainer();
        
        List<Animal> animalList = new ArrayList<Animal>();
        animalList.add(new Dog("Bob"));
        animalList.add(new Dog("Amy"));
                
        animalTrainer.act(animalList);
    }
}

The second version of the main class, Circus, is shown in Listing 2-3. The AnimalTrainer commands a list of birds to jump. We reason that it will be fine since a bird is actually an animal and can jump. This version of the Circus class, however, does not compile. The line with trouble is  animalTrainer.act(birdList); (Line 15) The error message is:
The method act(List<Animal>) in the type AnimalTrainer is not applicable for the arguments (List<Bird>)


Listing 2-3 - Circus Version 2
package nowildcard;
import java.util.ArrayList;
import java.util.List;

import animals.Bird;

public class Circus {
    public static void main(String[] args) {
        AnimalTrainer animalTrainer = new AnimalTrainer();
        
        List<Bird> birdList = new ArrayList<Bird>();
        birdList.add(new Bird("Tim"));
        birdList.add(new Bird("Nancy"));

        animalTrainer.act(birdList);
    }
}

In fact, Java does not regard List<Bird> as a subtype of List<Animal>. So the act method only accepts List<Animal> as argument, rejecting List<Bird>. You might wonder why Java has such an “unreasonable” rule. Actually, this rule exists for a very good reason. Let’s see why.


What if List<Bird> Were Regarded as a Subtype of List<Animal>?

If Java regarded List<Bird> as a subtype of List<Animal>, we were going to have trouble. To show the trouble, let’s update the Circus class as in Listing 3-3. At first lets look at the two new classes newly added, Magician as in Listing 3-1, and BirdTrainer as in Listing 3-2. The act method of Magician takes a list of Animals, and replaces all Animals in the list by two Dogs. The Magician class compiles perfectly. The act method of BirdTrainer takes a list of Birds and commands each of them to fly. The BirdTrainer class as well compiles perfectly. The Circus class (Version 3, Listing 3-3) does not compile for the same reason as in the Version 2 (Listing 2-3). Line 16 fails. The error message is:
The method act(List<Animal>) in the type Magician is not applicable for the arguments (List<Bird>)  

The Java compiler rejects Line 16 for a good reason. If Java regarded List<Bird> as a subtype of List<Animal> and allowed Line 16 compile, we were going to have run time exception because at run time, when the BirdTrainer got a list, the list actually would contain two Dogs. The Dogs would be commanded to fly but they cannot (a Dog object does not have a fly method). In other words, the Java type integrity would be broken.

To prevent trouble of this nature, Java does not regard List<Bird> as a subtype of List<Animal>. In a generic term of generics, it is said that the type parameter T in List<T> is non-variant.

Listing 3-1 - Magician Version 1

package whatif;
import java.util.List;

import animals.Animal;
import animals.Dog;

public class Magician {
    public void act(List<Animal> animalList) {
        for (int i = 0; i < animalList.size(); i++) {
            animalList.remove(i);
        }
        
        animalList.add(new Dog("Green"));
        animalList.add(new Dog("Red"));        
    }
}

Listing 3-2 - BirdTrainer Version 1

package whatif;
import java.util.List;

import animals.Bird;

public class BirdTrainer {
    public void act(List<Bird> birdList) {
        for (Bird bird : birdList) {
            bird.fly();
        }
    }
}

Listing 3-3 - Circus Version 3

package whatif;
import java.util.ArrayList;
import java.util.List;

import animals.Bird;

public class Circus {
    public static void main(String[] args) {
        BirdTrainer birdTrainer = new BirdTrainer();
        Magician magician = new Magician();
        
        List<Bird> birdList = new ArrayList<Bird>();
        birdList.add(new Bird("Tim"));
        birdList.add(new Bird("Nancy"));
        
        magician.act(birdList);    // this line won't compile.
        
        birdTrainer.act(birdList);
    }
}
 
Type Wildcard with a Upper Bound

Now since we understand why List<Bird> is not regarded as a subtype of List<Animal>, we can come back to seek a solution for our situation: How to enable an AnimalTrainer to accept a List<Bird> as well as a List<Animal>. Java’s solution is type wildcard. Specifically, we can change the AnimalTrainer class to be like in Listing 4-1 (Line 7: act(List<? extends Animal> animalList)). The ? in Line 7 is called a type wildcard, and ? extends Animal says Animal is the upper bound of the type wildcard.
With this version of AnimalTrainer, the main class, Circus, as in Listing 4-2 (it is essentially the same as in Listing 2-3) compiles and runs successfully.

Listing 4-1 - AnimalTrainer Version 2, With Type Wildcard With Upper Bound

package upperbound;
import java.util.List;

import animals.Animal;

public class AnimalTrainer {
    public void act(List<? extends Animal> animalList) {
        for (Animal animal : animalList) {
            animal.jump();
        }
    }
}

Listing 4-2 - Circus Version 4

package upperbound;
import java.util.ArrayList;
import java.util.List;

import animals.Bird;

public class Circus {
    public static void main(String[] args) {
        AnimalTrainer animalTrainer = new AnimalTrainer();
        
        List<Bird> birdList = new ArrayList<Bird>();
        birdList.add(new Bird("Tim"));
        birdList.add(new Bird("Nancy"));
        
        animalTrainer.act(birdList);
    }
}

In general, there are three rules about type wildcard with a upper bound, given a generic type G (e.g. List<E>), and two concrete types X and Y where Y is a subtype of X (e.g. Bird is a subtype of Animal):
  1. G<? extends Y> is subtype of G<? extends X> (e.g. List<? extends Bird> is a subtype of List<? extends Animal>
  2. G<X> is subtype of G<? extends X> (e.g. List<Bird> is a subtype of List<? extends Bird>)
  3. G<?> is just a shorthand for G<? extends Object> (e.g. List<?> is a shorthand for List<? extends Object>)
(For the formal specification, see 4.5.1.1 Type Argument Containment and Equivalence, The Java Language Specification, Third Edition)



(Those rules have something to do with the idea of covariance in the theory of generics. They however do not align very well with the theory.)


In the context of our example, Bird is a subtype of Animal, so List<? extends Bird> is a subtype of List<? extends Animal>. Meanwhile, List<Bird> is a subtype of List<? extends Bird>. Therefore List<Bird> is a subtype of List<? extends Animal>. That 

is the reason why the act method has a formal parameter of the type List<? extends Animal> and can accept List<Bird> and List<Animal> as argument.

You might wonder whether the Magician class can also be updated with type wildcard and bring back the trouble of messing up birds by dogs. It cannot. The catch is that when the act method of the Magician is updated to take a parameter of the type List<? extends Animal>, as in Listing 4-3 (Line 8), it is no longer allowed to call the add method of the List class. The compile error message is "The method add(capture#3-of ? extends Animal) in the type List<capture#3-of ? extends Animal> is not applicable for the arguments (Dog)". Therefore, it cannot add dogs into the list.  It fails because the add method here expects an argument of the type “? extends Animal” but Dog is not a subtype of it (Note: even though List<Dog> is a subtype of List<? extends Animal>, Dog is not a subtype of "? Extends Animal". More generally, no class is regard as a subtype of “? Extends Animal”.  It is said that “? Extends Animal” is an undefined type (Note: meanwhile, List<? Extends Animal> is a
well defined parameterized type.)

Listing 4-3 Magician Version 2


package upperbound;
import java.util.List;

import animals.Animal;
import animals.Dog;

public class Magician {
    public void act(List<? extends Animal> animalList) {
        for (int i = 0; i < animalList.size(); i++) {
            animalList.remove(i);
        }
        
        animalList.add(new Dog("Green")); // now these two lines do not compile
        animalList.add(new Dog("Red"));    
    }
}

Based on my experience, the general rule is like this: When a method with a parameter of a parameterized type that is one with type wildcard with a upper bound (e.g. act(List<? extends Animal>)), inside the body of the method, it is not allowed to call any method on the parameter object unless the method is parameterless (e.g. add(E element) on List<E>). I tried to find something about it in the Java Language Specification but found nothing. However, it seems that all Java compilers that I saw are implemented in this way. I could not figure out how to derive this rule from other more basic rules in the Java Specification.

Type Wildcard with a Lower Bound

Similar to type wildcard with a upper bound, a type wildcard may have a lower bound. (However, a type wildcard cannot have both upper and lower bound, nor have more than one upper or lower bounds). The syntax is like this: List<? super Bird> where Bird is the lower bound of the type wildcard.

In general, there are also three rules about type wildcard with a lower bound, given a generic type G (e.g. List<E>), and two concrete types X and Y where X is a subtype of Y (e.g. Bird is a subtype of Animal):
  1. G<? super Y> is a subtype of G<? super X> (e.g. List<? super Animal> is a subtype of List<? super Bird>
  2. G<X> is subtype of G<? super X> (e.g. List<Bird> is a subtype of List<? super Bird>)
(Again, for the formal specification, see 4.5.1.1 Type Argument Containment and Equivalence, The Java Language Specification, Third Edition)

(Those rules have something to do with the idea of contra-variance in the theory of generics. They however do not align very well with the theory.)

Let's illustrate the uses of type wildcard with a lower bound with another version of our Circus program as in Listing 5-1. In this version of the Circus class, an animalList (of type List<Animal>) and a birdList (of type List<Bird>) are created in the main method. Instead of adding animals or birds to the lists in the main method, the animalList is passed to the addBirds method of a BirdKeeper object, and the addDogs method of a DogKeeper object, to add birds and dogs into it. Then an AnimalTrainer acts on the animalList by commanding the animals (birds and dogs) in the list to jump. Also, the birdList is passed to the addBirds method of the BirdKeeper object to add birds to it.  And a BirdTrainer acts on the list by commanding the birds in the birdList to fly. The pertinent version of AnimalTrainer and BirdTrainer are shown in Listing 5-2 and 5-3.

Notice the following points in the above example. For the animalList to contain both birds and dogs, it must be defined as of type List<Animal>, instead of List<Bird> or List<Dog>. For the BirdTrainer to command birds in the birdList to fly, the birdList must be defined as of type List<Bird>, instead of List<Animal>.

For the program to work, the addBirds method of the BirdKeeper class must be able to:
  1. accept a List<Animal> as argument (Line 21, the Circus class)
  2. accept a List<Bird> as argument (Line 26, the Circus class)
  3. add birds into the List<Animal> and the List<Bird> (Line 7-8, the BirdKeeper class)
The solution is shown in Listing 5-4. The type of the formal parameter to the addBirds method is List<? super Bird>. The parameterized type that servers as the type of the formal parameter is a type wildcard with a lower bound. Other choices won't work. If we had the method header as public void addBirds(List<Animal> animalList), the method would not accept List<Bird> as argument; if we If we had the method header as public void addBirds(List<Bird> animalList), the method would not accept List<Animal> as argument; If we had the method header as public void addBirds(List<? extends Animal> animalList), the method would not be able to add anything to the list (i.e. animalList). A type wildcard with a lower bound is the necessary solution to this case.  

Notice that it is allowed to call a method with parameters (e.g the add method of List<E>) on an object referenced by a formal parameter of type wildcard with a lower bound (e.g. the parameter named animalList in the addBirds method of the BirdKeeper class), while it is not allowed if the wildcard has a upper bound.

Similarly, the DogKeeper class is shown in Listing 5-5.

Listing 5-1 - Circus Version 5

package lowerbound;
import java.util.ArrayList;
import java.util.List;

import animals.Animal;
import animals.Bird;

public class Circus {
    public static void main(String[] args) {
        AnimalTrainer animalTrainer = new AnimalTrainer();
        BirdTrainer birdTrainer = new BirdTrainer();
        
        List<Animal> animalList = new ArrayList<Animal>();
        
        BirdKeeper birdKeeper = new BirdKeeper();
        birdKeeper.addBirds(animalList);
               
        DogKeeper dogKeeper = new DogKeeper();        
        dogKeeper.addDogs(animalList);
                
        animalTrainer.act(animalList);
        
        List<Bird> birdList = new ArrayList<Bird>();
        birdKeeper.addBirds(birdList);
        
        birdTrainer.act(birdList);
    }
}

Listing 5-2 - AnimalTrainer Version 3

package lowerbound;
import java.util.List;
import animals.Animal;

public class AnimalTrainer {
    public void act(List<Animal> animalList) {
        for (Animal animal : animalList) {
            animal.jump();
        }
    }
}

Listing 5-3 - BirdTrainer Version 2

package lowerbound;
import java.util.List;
import animals.Bird;

public class BirdTrainer {
    public void act(List<Bird> birdList) {
        for (Bird bird : birdList) {
            bird.fly();
        }
    }
}


Listing 5-4 - BirdKeeper
package lowerbound;
import java.util.List;
import animals.Bird;

public class BirdKeeper {
    public void addBirds(List<? super Bird> animalList) {
        animalList.add(new Bird("Tim"));
        animalList.add(new Bird("Nancy"));
    }
}

Listing 5-5 - DogKeeper

package lowerbound;
import java.util.List;
import animals.Dog;

public class DogKeeper {
    public void addDogs(List<? super Dog> animalList) {
        animalList.add(new Dog("Bob"));
        animalList.add(new Dog("Amy"));
    }
}

Finally, notice that the addBirds method of the BirdKeeper class won't accept an argument of the type List<Nightingale> because List<Nightingale> is not regarded as a subtype of List<? super Bird>. The version of the Circus class as shown in Listing 5-5 won't compile. Line 30, birdKeeper.addBirds(nightingaleList);, will fail. The compile error is:
The method addBirds(List<? super Bird>) in the type BirdKeeper is not applicable for the arguments (List<nightingale>)

Listing 5-5 - Circus Version 6

package lowerbound;
import java.util.ArrayList;
import java.util.List;
import animals.Animal;
import animals.Bird;
import animals.Nightingale;

public class Circus {
    public static void main(String[] args) {
        AnimalTrainer animalTrainer = new AnimalTrainer();
        BirdTrainer birdTrainer = new BirdTrainer();
        
        List<Animal> animalList = new ArrayList<Animal>();
        
        BirdKeeper birdKeeper = new BirdKeeper();
        birdKeeper.addBirds(animalList);
        
        
        DogKeeper dogKeeper = new DogKeeper();        
        dogKeeper.addDogs(animalList);
                
        animalTrainer.act(animalList);
        
        List<Bird> birdList = new ArrayList<Bird>();
        birdKeeper.addBirds(birdList);
        
        birdTrainer.act(birdList);
        
        List<Nightingale> nightingaleList = new ArrayList<Nightingale>();
        birdKeeper.addBirds(nightingaleList); // does not compile
    }
}

Conclusion
  1. Type wildcard is used to increase flexibility of methods that take parameters of parameterized types
  2. It is not allowed to call a method on an object referenced by a parameter of a parameterized type with type wildcard with a upper bound unless the method is parameter less.
  3. It is  allowed to call any method on an object referenced by a parameter of a parameterized type with type wildcard with a lower bound.
References 
  • The Java Language Specification, Third Edition, by James Gosling, Bill Joy, Guy Steele, and Gilad Bracha, Addison Wesley Professional, 2005
  • The Java Programming Language, 4th Edition, by Ken Arnold, James Gosling, and David Holmes, Prentice Hall PTR, 2005

Tuesday, November 29, 2011

The Basics of Java Generics

Overview

For the sake of generics, Java types (classes and interfaces) can be grouped into three categories:
  • Ordinary type, e.g. String, Integer
  • Generic type, e.g. java.lang.Comparable<T>, java.util.List<E> , and java.util.ArrayList<E>
  • Parameterized type, e.g. java.lang.Comparable<Integer>, java.util.List<String>, and java.util.ArrayList<String>
In Java, there are four kinds of generic constructs:
  • generic interface
  • generic class
  • generic method
  • generic constructor
Constructors are very much like methods, except that there is not any return for constructors. For this reason, we are going to omit any discussion about generic constructors since all discussions about generic methods, except what about method returns, also apply to generic constructors.

Coding with generics usually involves one or more of the following:
  • Defining a generic interface, class, or method
  • Invoking a generic interface, or class
  • Invoking a generic method
  • Defining a non-generic method with at least a parameter of a generic type, or with the return of a generic type. Such a method must be a member of a generic type
  • Defining a method with at least a parameter of a parameterized type, or with the return of a parameterized type
  • Invoking a method with at least a parameter of a parameterized type
  • Invoking a method with the return of a parameterized type

Defining Generic Types

Example 1 – Defining a generic interface

public interface Iterable<T>

The T in Iterable<T> is called a type parameter. In the language of Java generics, we say that the generic type Iterable takes a type parameter, T. Conventionally, a single upper case T is used as identifier for a type parameter (T stands for type).

Example 2 – Defining a generic interface that extends another generic interface

public interface List<E> extends Collection<E>

Here the E in List<E> is the type parameter. Conventionally, E is used as identifier for type parameter of collections. (E stands for element)

Example 3 – Defining a generic class that implements a generic interface

public class ArrayList<E> implements List<E>


Defining Generic Method

Example  4 – Defining a generic method

<T> T[] toArray(T[] a);

Above is the definition of a toArray method in the body of java.util.List. The first <T> tells that this is a generic method and the method takes a type parameter T. This means that this method has a hole that will be filled later with a concrete type. Then it also tells that the type of the method return is T[],  and the type of the method parameter is a T[] (array of T). Essentially, the type parameter of this method establishes a constrain, in term of type, between the method return and parameters. If we want to turn a list into a String [], we must pass to the toArray method a String[].

Please note that a method whose parameters or return is of a type parameter is not necessarily a generic method unless in its definition  <T>  is placed before its return type (or void).  For example, the methods shown in Example 5 below are not generic methods.

On the other hand, even a non-generic type may have a generic method as its member.


Type Parameter

A type parameter is a placeholder for a concrete type.  It is important to understand that a type parameter is either taken by a generic type,  a generic method, or a generic constructor. On the other hand a generic type or method takes at least one type parameter.

Inside the body of a generic interface or class, a type parameter taken by the interface or class, can server as the type of parameters, the type of return, or the type of local variables, of an instance method. It can also server as the type of instance fields.

A type parameter taken by a generic method can server as type of its parameters, type of its return, or type of its local variables. (Note: It is legal for a generic method to be a static member of a class or interface)

A type parameter taken by a class or interface cannot be:
  • Type of its static fields (because there is only one class vs. many different T)
  • Anywhere in its static member methods (same reason)
  • In a static initial block (same reason)

In addition, none type parameter can be
  • used in new T() statement  to create a new object (because erasure)
  • used in new T[size]() to create a new array of objects (because erasure)

Example 5 - Defining methods with parameters or return of type parameter

boolean add(E e);
E get(int index);

The above two methods are defined in the body of generic List<E>, the type parameter E servers as the type of parameter named e for the method named add, and the return type of the method named get. These two methods are not generic methods. The type parameter E is not taken by the methods but by their owner type (i.e. List<E>).


Generic Type v.s.  Parameterized Type

It is critical to understand the difference and relationship between generic type and parameterized type. For example, ArrayList<E> is a generic type and  ArrayList<String> is a parameterized type.  They differ in the following aspects:
  • E in  ArrayList<E>  is a type parameter, and String  in  ArrayList<String> is an concrete type (particularly, a ordinary class). In regard to  ArrayList<String>, the concrete type String servers as the type argument, to fill the place held by type parameter E, which is taken by  ArrayList<E>.
  • A parameterized type, like an ordinary type, is a concrete type, while a generic type is an abstract type
  • It is legal to create an object of ArrayList<String> via statement new ArrayList<String>();, statement new ArrayList<E>(); is, however, illegal.
  • More generally, the usage of a parameterized type is exactly the same as an ordinary type. A parameterized type can be used at any place where an ordinary type is to be used, i.e. to be used as the type of a variable or a method return. The variable may be a method parameter, a local variable, or a field.
A parameterized type always has a special relationship with a generic type: a parameterized type is always instantiated out of a generic type. For example, ArrayList<String> is instantiated out of ArrayList<E>, by replacing a type argument, String, for the type parameter, E. In order for ArrayList<String> to exist, ArrayList<E> must exist first.

A type parameter is like a hole. When the hole is filled with a concrete type, a parameterized type comes out of the generic type. Replacing a type parameter by a concrete type is called invocation of a generic type. While one can invoke a method passing arguments, one can invoke a generic type passing type arguments.

In a parameterized type a type argument takes all places used to be held by its corresponding type parameter.  For example, in List<String>, there are effectively
boolean add(String e);
String get(int index);

(For the formal specification, see 4.5.2 Members and Constructors of Parameterized Types, The Java Language Specification, Third Edition, Addision Wesley, 2004)


Bounded Type Parameter

In a generic type definition, a type parameter may be given an upper bound.


Example 6 – Defining a generic type named SortedSet which is a set with elements sorted
 

public interface SortedSet<E extends Comparable<E>> extends Set<E>
 

Here <E extends Comparable<E>> indicates that E is a bounded type parameter and Comparable<E> is the upper bound. Any parameterized type out of this generic type must have the type argument as a sub-type of Comparable<E>.  For example, we may have a parameterized type SortedSet<Integer>. It is OK since Integer implements Comparable<Integer>. However, we cannot have a parameterized type SortedSet<java.io.File> because File does not implement Comparable<File>. In short, the bound of a type parameter is used to restrict the type arguments to the generic type. Without a bound, any type will be accepted as legal type argument at compile time. Some of them may lead to runtime exception.
 

A few more words about the example, the upper bound, java.lang.Comparable<E>,  is also a generic type, and its type parameter is E, the same as of SortedSet.

If there are multiple such bounds, separate them by & in the generic type definition.




Calling a Generic Method


Example 7 - Calling a generic method


         List<String> list = new ArrayList<String>();
         list.add("One");
         list.add("Two");
       
         String[] stringArray = list.toArray(new String[]{});
       
         System.out.println(stringArray[0]);
         System.out.println(stringArray[1]);

Usually, it is not required to specifying the type argument (i.e. the concrete type to take the place of the type parameter) when call a generic method, as the example above shows, because the compiler can infer the type argument from the type of the argument to the method (i.e. String[] in the example). That is however, not always the case. In some cases, the compiler cannot determine the concrete type by inference. Then the type argument has to be explicitly specified. The right syntax to specify the type argument to a generic method is show in the example below:


String[] stringArray = list.<String>toArray(new String[]{});
 

The type argument (e.g. String in the example above) is place between < and >, and immediately before the name of the generic method being called.

By the way, be aware that the toArray method does not bring complete type-safety. The following code fragment compiles but causes run time exception.


        List<String> list = new ArrayList<String>();
        list.add("One");
        list.add("Two");
        

        Integer[] intArray = list.toArray(new Integer[]{});

Type Wildcard

Type wildcard in Java Generics is a complex topic. It is discussed in my other post Type Wildcard in Java Generics.