Generics Framework - Platform - Java 8 Pocket Guide (2014)

Java 8 Pocket Guide (2014)

Part II. Platform

Chapter 16. Generics Framework

The Generics Framework, introduced in Java SE 5.0 and updated in Java SE 7 and 8, provides support that allows for the parameterization of types.

The benefit of generics is the significant reduction in the amount of code that needs to be written when developing a library. Another benefit is the elimination of casting in many situations.

The classes of the Collections Framework, the class Class, and other Java libraries have been updated to include generics.

See Java Generics and Collections by Maurice Naftalin and Philip Wadler (O’Reilly, 2006) for comprehensive coverage of the Generics Framework.

Generic Classes and Interfaces

Generic classes and interfaces parameterize types by adding a type parameter within angular brackets (i.e., <T>). The type is instantiated at the place of the brackets.

Once instantiated, the generic parameter type is applied throughout the class for methods that have the same type specified. In the following example, the add() and get() methods use the parameterized type as their parameter argument and return types, respectively:

public interface List <E> extends Collection<E>{

public boolean add(E e);

E get(int index);

}

When a variable of a parameterized type is declared, a concrete type (i.e., <Integer>) is specified to be used in place of the type parameter (i.e., <E>).

Subsequently, the need to cast when retrieving elements from things such as collections would be eliminated:

// Collection List/ArrayList with Generics

List<Integer> iList = new ArrayList<Integer>();

iList.add(1000);

// Explicit cast not necessary

Integer i = iList.get(0);

// Collection List/ArrayList without Generics

List iList = new ArrayList();

iList.add(1000);

// Explicit cast is necessary

Integer i = (Integer)iList.get(0);

The diamond operator <> was introduced in Java SE 7 to simplify the creation of generic types, by reducing the need for additional typing:

// Without the use of the diamond operator

List<Integer> iList1 = new ArrayList<Integer>();

// With the use of the diamond operator

List<Integer> iList2 = new ArrayList<>();

Constructors with Generics

Constructors of generic classes do not require generic type parameters as arguments:

// Generic Class

public class SpecialList <E> {

// Constructor without arguments

public SpecialList() {...}

public SpecialList(String s) {...}

}

A generic object of this class could be instantiated as such:

SpecialList<String> b = new

SpecialList<String>();

If a constructor for a generic class includes a parameter type such as a String, the generic object could be instantiated as such:

SpecialList<String> b = new

SpecialList<String>("Joan Marie");

Substitution Principle

As specified in Java Generics and Collections (O’Reilly), the Substitution Principle allows subtypes to be used where their supertype is parameterized:

§ A variable of a given type may be assigned a value of any subtype of that type.

§ A method with a parameter of a given type may be invoked with an argument of any subtype of that type.

Byte, Short, Integer, Long, Float, Double, BigInteger, and BigDecimal are all subtypes of class Number:

// List declared with generic Number type

List<Number> nList = new ArrayList<Number>();

nList.add((byte)27); // Byte (Autoboxing)

nList.add((short)30000); // Short

nList.add(1234567890); // Integer

nList.add((long)2e62); // Long

nList.add((float)3.4); // Float

nList.add(4000.8); // Double

nList.add(new BigInteger("9223372036854775810"));

nList.add(new BigDecimal("2.1e309"));

// Print Number's subtype values from the list

for( Number n : nList )

System.out.println(n);

Type Parameters, Wildcards, and Bounds

The simplest declaration of a generic class is with an unbounded type parameter, such as T:

public class GenericClass <T> {...}

Bounds (constraints) and wildcards can be applied to the type parameter(s), as shown in Table 16-1.

Table 16-1. Type parameters, bounds, and wildcards

Type parameters

Description

<T>

Unbounded type; same as <T extends Object>

<T,P>

Unbounded types; <T extends Object> and <P extends Object>

<T extends P>

Upper bounded type; a specific type T that is a subtype of type P

<T extends P & S>

Upper bounded type; a specific type T that is a subtype of type P and that implements type S

<T super P >

Lower bounded type; a specific type T that is a supertype of type P

<?>

Unbounded wildcard; any object type, same as <? extends Object>

<? extends P>

Bounded wildcard; some unknown type that is a subtype of type P

<? extends P & S>

Bounded wildcard; some unknown type that is a subtype of type P and that implements type S

<? super P>

Lower bounded wildcard; some unknown type that is a supertype of type P

The Get and Put Principle

As also specified in Java Generics and Collections, the Get and Put Principle details the best usage of extends and super wildcards:

§ Use an extends wildcard when you get only values out of a structure.

§ Use a super wildcard when you put only values into a structure.

§ Do not use a wildcard when you place both get and put values into a structure.

The extends wildcard has been used in the method declaration of the addAll() method of the List collection, as this method gets values from a collection:

public interface List <E> extends Collection<E>{

boolean addALL(Collection <? extends E> c)

}

List<Integer> srcList = new ArrayList<Integer>();

srcList.add(0);

srcList.add(1);

srcList.add(2);

// Using addAll() method with extends wildcard

List<Integer> destList = new ArrayList<Integer>();

destList.addAll(srcList);

The super wildcard has been used in the method declaration of the addAll() method of the class Collections, as the method puts values into a collection:

public class Collections {

public static <T> boolean addAll

(Collection<? super T> c, T... elements){...}

}

// Using addAll() method with super wildcard

List<Number> sList = new ArrayList<Number>();

sList.add(0);

Collections.addAll(sList, (byte)1, (short)2);

Generic Specialization

A generic type can be extended in a variety of ways.

Given the parameterized abstract class AbstractSet <E>:

class SpecialSet<E> extends AbstractSet<E> {…}

The SpecialSet class extends the AbstractSet class with the parameter type E. This is the typical way to declare generalizations with generics.

class SpecialSet extends AbstractSet<String> {…}

The SpecialSet class extends the AbstractSet class with the parameterized type String.

class SpecialSet<E,P> extends AbstractSet<E> {…}

The SpecialSet class extends the AbstractSet class with the parameter type E. Type P is unique to the SpecialSet class.

class SpecialSet<E> extends AbstractSet {…}

The SpecialSet class is a generic class that would parameterize the generic type of the AbstractSet class. Because the raw type of the AbstractSet class has been extended (as opposed to generic), the parameterization cannot occur. Compiler warnings will be generated upon method invocation attempts.

class SpecialSet extends AbstractSet {…}

The SpecialSet class extends the raw type of the AbstractSet class. Because the generic version of the AbstractSet class was expected, compiler warnings will be generated upon method invocation attempts.

Generic Methods in Raw Types

Static methods, nonstatic methods, and constructors that are part of nongeneric or raw type classes can be declared as generic. A raw type class is the nongeneric counterpart class to a generic class.

For generic methods of nongeneric classes, the method’s return type must be preceded with the generic type parameter (e.g., <E>). However, there is no functional relationship between the type parameter and the return type, unless the return type is of the generic type:

public class SpecialQueue {

public static <E> boolean add(E e) {...}

public static <E> E peek() {...}

}

When calling the generic method, the generic type parameter is placed before the method name. Here, <String> is used to specify the generic type argument:

SpecialQueue.<String>add("White Carnation");