Java Language


Generics Java SE 5–Java SE 9 (Early Access)

Java SE 1.0
Java SE 1.1
Java SE 1.2
Java SE 1.3
Java SE 1.4
Java SE 5
Java SE 6
Java SE 7
Java SE 8
Java SE 9 (Early Access)

This draft deletes the entire topic.

Introduction

Generics are a facility of generic programming that extend Java's type system to allow a type or method to operate on objects of various types while providing compile-time type safety. In particular, the Java collections framework supports generics to specify the type of objects stored in a collection instance.

expand all collapse all

Examples

  • 53

    Generics enable classes, interfaces, and methods to take other classes and interfaces as type parameters.

    This example uses generic class Param to take a single type parameter T, delimited by angle brackets (<>):

    public class Param<T> {
        private T value;
    
        public T getValue() {
            return value;
        }
    
        public void setValue(T value) {
            this.value = value;
        }
    }
    

    To instantiate this class, provide a type argument in place of T. For example, Integer:

    Param<Integer> integerParam = new Param<Integer>();
    

    The type argument can be any reference type, including arrays and other generic types:

    Param<String[]> stringArrayParam;
    Param<int[][]> int2dArrayParam;
    Param<Param<Object>> objectNestedParam;
    

    In Java SE 7 and later, the type argument can be replaced with an empty set of type arguments (<>) called the diamond:

    Java SE 7
    Param<Integer> integerParam = new Param<>();
    

    Unlike other identifiers, type parameters have no naming constraints. However their names are commonly the first letter of their purpose in upper case. (This is true even throughout the official JavaDocs.)
    Examples include T for "type", E for "element" and K/V for "key"/"value".


    Extending a generic class

    public abstract class AbstractParam<T> {
        private T value;
    
        public T getValue() {
            return value;
        }
    
        public void setValue(T value) {
            this.value = value;
        }
    }
    

    AbstractParam is an abstract class declared with a type parameter of T. When extending this class, that type parameter can be replaced by a type argument written inside <>, or the type parameter can remain unchanged. In the first and second examples, String and Integer replace the type parameter. In the third example, the type parameter remains unchanged. The fourth example doesn't use generics at all, so it's similar to if the class had an Object parameter. The compiler will warn about AbstractParam being a raw type, but it will compile the ObjectParam class. The fifth example has 2 type parameters (see "multiple type parameters" below), choosing the second parameter as the type parameter passed to the superclass.

    public class Email extends AbstractParam<String> {
        // ...
    }
    
    public class Age extends AbstractParam<Integer> {
        // ...
    }
    
    public class Height<T> extends AbstractParam<T> {
        // ...
    }
    
    public class ObjectParam extends AbstractParam {
        // ...
    }
    
    public class MultiParam<T, E> extends AbstractParam<E> {
        //...
    }
    

    The following is the usage:

    Email email = new Email();
    email.setValue("[email protected]");
    String retrievedEmail = email.getValue();
    
    Age age = new Age();
    age.setValue(25);
    Integer retrievedAge = age.getValue();
    int autounboxedAge = age.getValue();
    
    Height<Integer> heightInInt = new Height<>();
    heightInInt.setValue(125);
    
    Height<Float> heightInFloat = new Height<>();
    heightInFloat.setValue(120.3f);
    
    MultiParam<String, Double> multiParam = new MultiParam<>();
    multiParam.setValue(3.3);
    

    Notice that in the Email class, the T getValue() method acts as if it had a signature of String getValue(), and the void setValue(T) method acts as if it was declared void setValue(String).

    It is also possible to instantiate with anonymous inner class with an empty curly braces ({}):

    AbstractParam<Double> height = new AbstractParam<Double>(){};
    height.setValue(198.6);
    

    Note that using the diamond with anonymous inner classes is not allowed.


    Multiple type parameters

    Java provides the ability to use more than one type parameter in a generic class or interface. Multiple type parameters can be used in a class or interface by placing a comma-separated list of types between the angle brackets. Example:

    public class MultiGenericParam<T, S> {
        private T firstParam;
        private S secondParam;
       
        public MultiGenericParam(T firstParam, S secondParam){
            this.firstParam = firstParam;
            this.secondParam = secondParam;
        }
        
        public T getFirstParam(){
            return firstParam;
        }
        
        public void setFirstParam(T firstParam){
            this.firstParam = firstParam;
        }
        
        public S getSecondParam(){
            return secondParam;
        }
        
        public void setSecondParam(S secondParam){
            this.secondParam = secondParam;
        }
    }
    

    The usage can be done as below:

    MultiGenericParam<String, String> aParam = new MultiGenericParam<String, String>("value1", "value2");
    MultiGenericParam<Integer, Double> dayOfWeekDegrees =  new MultiGenericParam<Integer, Double>(1, 2.6);
    
  • 29
    Java SE 7

    Java 7 introduced the Diamond1 to remove some boiler-plate around generic class instantiation. With Java 7+ you can write:

    List<String> list = new LinkedList<>();
    

    Where you had to write in previous versions, this:

    List<String> list = new LinkedList<String>();
    

    One limitation is for Anonymous Classes, where you still must provide the type parameter in the instantiation:

    // This will compile:
    
    Comparator<String> caseInsensitiveComparator = new Comparator<String>() {
        @Override
        public int compare(String s1, String s2) {
            return s1.compareToIgnoreCase(s2);
        }
    };
    
    // But this will not:
    
    Comparator<String> caseInsensitiveComparator = new Comparator<>() {
        @Override
        public int compare(String s1, String s2) {
            return s1.compareToIgnoreCase(s2);
        }
    };
    
    Java SE 8

    Although using the diamond with Anonymous Inner Classes is not supported in Java 7 and 8, it will be included as a new feature in Java 9.


    Footnote:

    1 - Some people call the <> usage the "diamond operator". This is incorrect. The diamond does not behave as an operator, and is not described or listed anywhere in the JLS or the (official) Java Tutorials as an operator. Indeed, <> is not even a distinct Java token. Rather it is a < token followed by a > token, and it is legal (though bad style) to have whitespace or comments between the two. The JLS and the Tutorials consistently refer to <> as "the diamond", and that is therefore the correct term for it.

  • 24

    The syntax for Java generics bounded wildcards, representing the unknown type by ? is:

    • ? extends T represents an upper bounded wildcard. The unknown type represents a type that must be a subtype of T, or type T itself.

    • ? super T represents a lower bounded wildcard. The unknown type represents a type that must be a supertype of T, or type T itself.

    As a rule of thumb, you should use

    • ? extends T if you only need "read" access ("input")
    • ? super T if you need "write" access ("output")
    • T if you need both ("modify")

    Using extends or super is usually better because it makes your code more flexible (as in: allowing the use of subtypes and supertypes), as you will see below.

    class Shoe {}
    class IPhone {}
    interface Fruit {}
    class Apple implements Fruit {}
    class Banana implements Fruit {}
    class GrannySmith extends Apple {}
    
    void eatAll(Collection<? extends Fruit> fruits) {
        for(Fruit f : fruits) {
          // Eat.
        }
    }
    
    void addApple(Collection<? super Apple> apples) {
        apples.add(new GrannySmith());
    }
    

    The compiler will now be able to detect certain bad usage:

    void demo() {
        List<Fruit> fruits = new ArrayList<Fruit>();
        fruits.add(new Apple()); // Allowed, as Apple is a Fruit
        fruits.add(new Banana()); // Allowed, as Banana is a Fruit
        addApple(fruits); // Allowed, as "Fruit super Apple"
        eatAll(fruits); // Allowed
    
        Collection<Banana> bananas = new ArrayList<>();
        bananas.add(new Banana()); // Allowed
        //addApple(bananas); // Compile error: may only contain Bananas!
        eatAll(bananas); // Allowed, as all Bananas are Fruits
    
        Collection<Apple> apples = new ArrayList<>();
        addApple(apples); // Allowed
        apples.add(new GrannySmith()); // Allowed, as this is an Apple
        eatAll(apples); // Allowed, as all Apples are Fruits.
        
        Collection<GrannySmith> grannySmithApples = new ArrayList<>();
        //addApple(grannySmithApples); //Compile error: Not allowed.
                                       // GrannySmith is not a supertype of Apple
        apples.add(new GrannySmith()); //Still allowed, GrannySmith is an Apple
        eatAll(grannySmithApples);//Still allowed, GrannySmith is a Fruit
    
        Collection<Object> objects = new ArrayList<>();
        addApple(objects); // Allowed, as Object super Apple
        objects.add(new Shoe()); // Not a fruit
        objects.add(new IPhone()); // Not a fruit
        //eatAll(objects); // Compile error: may contain a Shoe, too!
    }
    

    Choosing the right T, ? super T or ? extends T is necessary to allow the use with subtypes. The compiler can then ensure type safety; you should not need to cast (which is not type safe, and may cause programming errors) if you use them properly.

    If it is not easy to understand, please remember PECS rule:

    Producer uses "Extends" and Consumer uses "Super".

    (Producer has only write access, and Consumer has only read access)

Please consider making a request to improve this example.

Syntax

  • class ArrayList<E> {} // a generic class with type parameter E
  • class HashMap<K, V> {} // a generic class with two type parameters K and V
  • <E> void print(E element) {} // a generic method with type parameter E
  • ArrayList<String> names; // declaration of a generic class
  • ArrayList<?> objects; // declaration of a generic class with an unknown type parameter
  • new ArrayList<String>() // instantiation of a generic class
  • new ArrayList<>() // instantiation with type inference "diamond" (Java 7 or later)

Parameters

Parameters

Remarks

Generics are implemented in Java through Type erasure, which means that during runtime the Type information specified in the instantiation of a generic class is not available. For example, the statement List<String> names = new ArrayList<>(); produces a list object from which the element type String cannot be recovered at runtime. However, if the list is stored in a field of type List<String>, or passed to a method/constructor parameter of this same type, or returned from a method of that return type, then the full type information can be recovered at runtime through the Java Reflection API.

This also means that when casting to a generic type (e.g.: (List<String>) list), the cast is an unchecked cast. Because the parameter <String> is erased, the JVM cannot check if a cast from a List<?> to a List<String> is correct; the JVM only sees a cast for List to List at runtime.

Still have a question about Generics? Ask Question

Topic Outline