Jump to content

Generics

100% developed
From Wikibooks, open books for an open world

Navigate Classes and Objects topic: v  d  e )

Java is a strongly typed language, so a field in a class may be typed like this:

Computer code Code listing 4.34: Repository.java
public class Repository {

   public Integer item;

   public Integer getItem() {
      return item;
   }

   public void setItem(Integer newItem) {
      item = newItem;
   }
}

This ensures that, only Integer objects can be put in the field and a ClassCastException can't occur at runtime, only compile-time error can occur. Unfortunately, it can be used only with Integer objects. If you want to use the same class in another context with Strings, you have to generalize the type like this:

Computer code Code listing 4.35: Repository.java
public class Repository {

   public Object item;

   public Object getItem() {
      return item;
   }

   public void setItem(Integer newItem) {
      item = newItem;
   }

   public void setItem(String newItem) {
      item = newItem;
   }
}

But you will have ClassCastException at runtime again and you can't easily use your field. The solution is to use Generics.

Generic class

[edit | edit source]

A generic class does not hard code the type of a field, a return value or a parameter. The class only indicates that a generic type should be the same, for a given object instance. The generic type is not specified in the class definition. It is specified during object instantiation. This allows the generic type to be different from an instance to another. So we should write our class this way:

Computer code Code listing 4.36: Repository.java
public class Repository<T> {

   public T item;

   public T getItem() {
      return item;
   }

   public void setItem(T newItem) {
      item = newItem;
   }
}

Here, the generic type is defined after the name of the class. Any new identifier can be chosen. Here, we have chosen T, which is the most common choice. The actual type is defined at the object instantiation:

Example Code section 4.35: Instantiation.
Repository<Integer> arithmeticRepository = new Repository<Integer>();
arithmeticRepository.setItem(new Integer(1));
Integer number = arithmeticRepository.getItem();

Repository<String> textualRepository = new Repository<String>();
textualRepository.setItem("Hello!");
String message = textualRepository.getItem();

Although each object instance has its own type, each object instance is still strongly typed:

Warning Code section 4.36: Compile error.
Repository<Integer> arithmeticRepository = new Repository<Integer>();
arithmeticRepository.setItem("Hello!");

A class can define as many generic types as you like. Choose a different identifier for each generic type and separate them by a comma:

Computer code Code listing 4.37: Repository.java
public class Repository<T, U> {

   public T item;

   public U anotherItem;

   public T getItem() {
      return item;
   }

   public void setItem(T newItem) {
      item = newItem;
   }

   public U getAnotherItem() {
      return anotherItem;
   }

   public void setAnotherItem(U newItem) {
      anotherItem = newItem;
   }
}

When a type that is defined with generic (for example, Collection<T>) is not used with generics (for example, Collection) is called a raw type.

Generic method

[edit | edit source]

A generic type can be defined for just a method:

Example Code section 4.37: Generic method.
public <D> D assign(Collection<D> generic, D obj) {
  generic.add(obj);
  return obj;
}

Here a new identifier (D) has been chosen at the beginning of the method declaration. The type is specific to a method call and different types can be used for the same object instance:

Example Code section 4.38: Generic method call.
Collection<Integer> numbers = new ArrayList<Integer>();
Integer number = assign(numbers, new Integer(1));
Collection<String> texts = new ArrayList<String>();
String text = assign(texts, "Store it.");

The actual type will be defined by the type of the method parameter. Hence, the generic type can't be defined only for the return value as it wouldn't be resolved. See the Class<T> section for a solution.

Test your knowledge

Question 4.8: Consider the following class.

Computer code Question 4.8: Question8.java
public class Question8<T> {
  public T item;
 
  public T getItem() {
    return item;
  }
 
  public void setItem(T newItem) {
    item = newItem;
  }

  public static void main(String[] args) {
    Question8<String> aQuestion = new Question8<String>();
    aQuestion.setItem("Open your mind.");
    aQuestion.display(aQuestion.getItem());
  }

  public void display(String parameter) {
    System.out.println("Here is the text: " + parameter);
  }

  public void display(Integer parameter) {
    System.out.println("Here is the number: " + parameter);
  }

  public void display(Object parameter) {
    System.out.println("Here is the object: " + parameter);
  }
}

What will be displayed on the console?

Answer
Standard input or output Console for Answer 4.8
Here is the text: Open your mind.

aQuestion.getItem() is typed as a string.

Wildcard Types

[edit | edit source]

As we have seen above, generics give the impression that a new container type is created with each different type parameter. We have also seen that in addition to the normal type checking, the type parameter has to match as well when we assign generics variables. In some cases this is too restrictive. What if we would like to relax this additional checking? What if we would like to define a collection variable that can hold any generic collection, regardless of the parameter type it holds? The wildcard type is represented by the character <?>, and pronounced Unknown, or Any-Type. Any-Type can be expressed also by <? extends Object>. Any-Type includes Interfaces, not only Classes. So now we can define a collection whose element type matches anything. See below:

Example Code section 4.39: Wildcard type.
Collection<?> collUnknown;

Upper bounded wildcards

[edit | edit source]

You can specify a restriction on the types of classes that may be used. For example, <? extends ClassName> only allows objects of class ClassName or a subclass. For example, to create a collection that may only contain "Serializable" objects, specify:

Example Code section 4.40: Collection of serializable subobjects.
Collection<String> textColl = new ArrayList<String>();

Collection<? extends Serializable> serColl = textColl;

The above code is valid because the String class is serializable. Use of a class that is not serializable would cause a compilation error. The added items can be retrieved as Serializable object. You can call methods of the Serializable interface or cast it to String. The following collection can only contain objects that extend the class Animal.

Computer code Code listing 4.38: Dog.java
class Dog extends Animal {
}
Example Code section 4.41: Example of subclass.
// Create "Animal Collection" variable
Collection<? extends Animal> animalColl = new ArrayList<Dog>();

Lower bounded wildcards

[edit | edit source]

<? super ClassName> specifies a restriction on the types of classes that may be used. For example, to declare a Comparator that can compare Dogs, you use:

Example Code section 4.42: Superclass.
Comparator<? super Dog> myComparator;

Now suppose you define a comparator that can compare Animals:

Example Code section 4.43: Comparator.
class AnimalComparator implements Comparator<Animal> {
  int compare(Animal a, Animal b) {
   //...
  }
}

Since Dogs are Animals, you can use this comparator to compare Dogs also. Comparators for any superclass of Dog can also compare Dog; but comparators for any strict subclass cannot.

Example Code section 4.44: Generic comparator.
Comparator<Animal> myAnimalComparator = new AnimalComparator();

static int compareTwoDogs(Comparator<? super Dog> comp, Dog dog1, Dog dog2) {
  return comp.compare(dog1, dog2);
}

The above code is valid because the Animal class is a supertype of the Dog class. Use of a class that is not a supertype would cause a compilation error.

Unbounded wildcard

[edit | edit source]

The advantage of the unbounded wildcard (i.e. <?>) compared to a raw type (i.e. without generic) is to explicitly say that the parameterized type is unknown, not any type. That way, all the operations that implies to know the type are forbidden to avoid unsafe operation. Consider the following code:

Example Code section 4.45: Unsafe operation.
public void addAtBottom(Collection anyCollection) {
  anyCollection.add(new Integer(1));
}

This code will compile but this code may corrupt the collection if the collection only contains strings:

Example Code section 4.46: Corruption of list.
List<String> col = new ArrayList<String>();
addAtBottom(col);
col.get(0).endsWith(".");
Standard input or output Console for Code section 4.46
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer incompatible with java.lang.String
at Example.main(Example.java:17)

This situation could have been avoided if the addAtBottom(Collection) method was defined with an unbounded wildcard: addAtBottom(Collection<?>). With this signature, it is impossible to compile a code that is dependent of the parameterized type. Only independent methods of a collection (clear(), isEmpty(), iterator(), remove(Object o), size(), ...) can be called. For instance, addAtBottom(Collection<?>) could contain the following code:

Example Code section 4.47: Safe operation.
public void addAtBottom(Collection<?> anyCollection) {
   Iterator<?> iterator = anyCollection.iterator();
   while (iterator.hasNext()) {
      System.out.print(iterator.next());
   }
}

Class<T>

[edit | edit source]

Since Java 1.5, the class java.lang.Class is generic. It is an interesting example of using generics for something other than a container class. For example, the type of String.class is Class<String>, and the type of Serializable.class is Class<Serializable>. This can be used to improve the type safety of your reflection code. In particular, since the newInstance() method in Class now returns T, you can get more precise types when creating objects reflectively. Now we can use the newInstance() method to return a new object with exact type, without casting. An example with generics:

Example Code section 4.48: Automatic cast.
Customer cust = Utility.createAnyObject(Customer.class);  // No casting
...
public static <T> T createAnyObject(Class<T> cls) {
    T ret = null;
    try {
        ret = cls.newInstance();
    } catch (Exception e) {
        // Exception Handling
    }
    return ret;
}

The same code without generics:

Example Code section 4.49: Former version.
Customer cust = (Customer) Utility.createAnyObject(Customer.class);  // Casting is needed
...
public static Object createAnyObject(Class cls) {
    Object ret = null;
    try {
        ret = cls.newInstance();
    } catch (Exception e) {
        // Exception Handling
    }
    return ret;
}

Motivation

[edit | edit source]

Java was long criticized for the need to explicitly type-cast an element when it was taken out of a "container/collection" class. There was no way to enforce that a "collection" class contains only one type of object (e.g., to forbid at compile time that an Integer object is added to a Collection that should only contain Strings). This is possible since Java 1.5. In the first couple of years of Java evolution, Java did not have a real competitor. This has changed by the appearance of Microsoft C#. With Generics Java is better suited to compete against C#. Similar constructs to Java Generics exist in other languages, see Generic programming for more information. Generics were added to the Java language syntax in version 1.5. This means that code using Generics will not compile with Java 1.4 and less. Use of generics is optional. For backwards compatibility with pre-Generics code, it is okay to use generic classes without the generics type specification (<T>). In such a case, when you retrieve an object reference from a generic object, you will have to manually cast it from type Object to the correct type.

Note for C++ programmers

[edit | edit source]

Java Generics are similar to C++ Templates in that both were added for the same reason. The syntax of Java Generic and C++ Template are also similar. There are some differences however. The C++ template can be seen as a kind of macro, in that a new copy of the code is generated for each generic type referenced. All extra code for templates is generated at compiler time. In contrast, Java Generics are built into the language. The same code is used for each generic type. For example:

Example Code section 4.50: Java generics.
Collection<String>  collString  = new ArrayList<String>();
Collection<Integer> collInteger = new ArrayList<Integer>();

Both these objects appear as the same type at runtime (both ArrayList's). The generic type information is erased during compilation (type erasure). For example:

Example Code section 4.51: Type erasure.
public <T> void method(T argument) {
  T variable;
  
}

is transformed by erasure into:

Example Code section 4.52: Transformation.
public void method(Object argument) {
  Object variable;
  
}
Test your knowledge

Question 4.9: Consider the following class.

Computer code Question 4.9: Question9.java
import java.util.ArrayList;
import java.util.Collection;

public class Question9 {
  public static void main(String[] args) {
    Collection<String> collection1 = new ArrayList<String>();
    Collection<? extends Object> collection2 = new ArrayList<String>();
    Collection<? extends String> collection3 = new ArrayList<String>();
    Collection<? extends String> collection4 = new ArrayList<Object>();
    Collection<? super Object> collection5 = new ArrayList<String>();
    Collection<? super Object> collection6 = new ArrayList<Object>();
    Collection<?> collection7 = new ArrayList<String>();
    Collection<? extends Object> collection8 = new ArrayList<?>();
    Collection<? extends Object> collection9 = new ArrayList<Object>();
    Collection<? extends Integer> collection10 = new ArrayList<String>();
    Collection<String> collection11 = new ArrayList<? extends String>();
    Collection collection12 = new ArrayList<String>();
  }
}

Which lines will generate a compile error?

Answer
Computer code Answer 4.9: Answer9.java
import java.util.ArrayList;
import java.util.Collection;

public class Answer9 {
  public static void main(String[] args) {
    Collection<String> collection1 = new ArrayList<String>();
    Collection<? extends Object> collection2 = new ArrayList<String>();
    Collection<? extends String> collection3 = new ArrayList<String>();
    Collection<? extends String> collection4 = new ArrayList<Object>();
    Collection<? super Object> collection5 = new ArrayList<String>();
    Collection<? super Object> collection6 = new ArrayList<Object>();
    Collection<?> collection7 = new ArrayList<String>();
    Collection<? extends Object> collection8 = new ArrayList<?>();
    Collection<? extends Object> collection9 = new ArrayList<Object>();
    Collection<? extends Integer> collection10 = new ArrayList<String>();
    Collection<String> collection11 = new ArrayList<? extends String>();
    Collection collection12 = new ArrayList<String>();
  }
}
  • Line 9: Object does not extend String.
  • Line 10: String is not a superclass of Object.
  • Line 13: ArrayList<?> can't be instantiated.
  • Line 15: Integer does not extend String.
  • Line 16: ArrayList<? extends String> can't be instantiated.