Generics is parameterised types denoted with a diamond operator <>
, it allows the creation of generic classes, interfaces, and methods that can work with different types. It promotes type safety, explicit type casting and code reusability since the same generic code can be used for multiple types without duplicating the implementation.
Type parameter naming convention
E
: Element (used extensively by the Java Collections Framework)T
: TypeK
: Key (i.e. in aMap<K,V>
for key-value pair)N
: NumberV
: ValueS
,U
,V
etc: 2nd, 3rd, 4th types
Generic method, class and interface
Generic method
Generic methods are methods that introduce their own type parameters. It follows the format:
<T, S>
: This declares two type parametersT
andS
that can be used in the method.T
: This specifies that the return type of the method isT
.T anyThing
: This is the first parameter of the method of typeT
.S anotherThing
: This is the second parameter of the method of typeS
.
Non-generic approach and its downside
Consider the below example that methods implemented using method overloading.
100 Hello 3D Point [1,2,3]
In this example we are using three overloaded methods for different types that performs the same functionality, to print the
thing
. This leads to bad code reuse because these method performs the same task just for serving different types.Temporary solution Because every class in Java derives the
Object
class, we can useObject
as parameter type. This can be a temporary solution.However, there are situations that it can cause type safety issue:
java: incompatible types: java.lang.Object cannot be converted to int
In this case you have to use explicit casting to tell JVM that this is an integer.
Now the method thingToPrint()
takes a generic type T
, you can technically name the generic type anything you want other than T
, but use the naming convention provides better readability. The type parameter T
can be any thing that is non-primitive (but meanwhile, Java provides autoboxing mechanism that automatically convert primitive types to their correspond wrapper type).
- In the first method call,
thingToPrint()
takes a primitiveint
, theint
will be auto converted toInteger
type. Now theT
is specified toInteger
as the parameterthing
is anInteger
. - In the second method call,
thingToPrint()
takes aString
object. TheT
is specified toString
. - In the third method call,
thingToPrint()
takes aPoint3D
object. TheT
is specified toPoint3D
.
Generic class
This is a sample of a generic class.
- The class declaration with
<T>
indicates that the class can work with a generic typeT
. T fieldName
declares a field of the generic typeT
.
Now, we create two instances of the class MyClass
with different constructor parameters.
String field: Hello
Integer field: 123
Generic interface
The generic interface also supports multiple types.
Wildcards
In Java generics, the question mark ?
used is called the wildcard. It is used when you don’t know or care about what the generic type is.
Different from other generic types such as <T>
that are used when you want to define classes, interfaces, or methods that operate on a specific type parameter, ensuring type safety and code reusability; wildcards ?
are used for flexibility when you want to handle collections of unknown types or types that can vary (extends
for subtypes or super
for super types).
Note
Wildcards can be used for reading but not for adding elements to a collection because of the type safety concerns, this will be discussed in the subsequent the bounded generics.
The below code printList()
method takes a list of unknown type. The wildcard ?
means the method can accept a list of any type.
one two three
1 2 3
Bounded type generics
Bounded type generics allows you to restrict the type that can be used as type argument in a generic class, method or interface. It enables you to specify that a type parameter must be a subtype of a particular class or implement specific interfaces. If an unrelated class is used as argument, a compilation error will be thrown.
Upper bounded generics
An upper bounded type parameter restricts the type argument to be a specific class or its subtypes. It is denoted by the extends
keyword.
In this sample, the type parameter T
must be SomeClass
or a subclass of SomeClass
.
The upper bounded generics also accepts multiple bounds for a type parameter.
In the below sample, the T
must be a subclass of SomeClass
and also implement SomeInterface
. You can restrict it with multiple interfaces with multiple &
signs.
You can also use upper bounded wildcard <? extends T>
to accept type T
or any subclass of T
. In the below example, you can use an upper bounded wildcard to write a method that can take a collection of Animal
or any subclass of Animal
.
In the forgoing description, wildcard is not for adding elements to a collection. According to this example, if you try to add an element to the list, the compiler can’t guarantee what specific subtype of Animal
is in the list. For example, if you have a List<Dog>
, you can’t add a Cat
to it.
Lower bounded generics
The lower bounded generics is used to specify the argument is the super type of a specific class. The lower bounded generics follows this format:
In the below example, the list can be of any type that is a super type of Integer
(e.g., Number
, Object
), and you can safely add Integer
elements to the list.
Back to parent page: Java Standard Edition (Java SE) and Java Programming
Web_and_App_DevelopmentProgramming_LanguagesJavaGenerics
Reference: