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. Generics over Primitive Types is targeted for Java SE 10.
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 Philip Wadler and Maurice Naftalin (O’Reilly, 2009) for comprehensive coverage of the Generics Framework.
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 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"
);
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
)
2
e62
);
// 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
);
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.
Type parameters | Description |
---|---|
|
Unbounded type; same as |
|
Unbounded types; |
|
Upper bounded type; a specific type |
|
Upper bounded type; a specific type |
|
Lower bounded type; a specific type |
<?> |
Unbounded wildcard; any object type, same as |
|
Bounded wildcard; some unknown type that is a subtype of type |
|
Bounded wildcard; some unknown type that is a subtype of type |
|
Lower bounded wildcard; some unknown type that is a supertype of type |
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
);
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.
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"
);