0% found this document useful (0 votes)
13 views

Week 5 Java Generics

Generics allow types to be parameters for classes and interfaces. This allows polymorphism where a generic class like List<E> can bind to any reference type like Integer or Double specified during instantiation. Wrapper classes like Integer box primitive types like int to make them reference types. Auto-boxing and unboxing allow primitive types to be used where reference types are expected. Mutable ArrayList<E> stores elements of type E. Immutable ImList<E> delegates to an encapsulated ArrayList while overriding add() to return a new instance, maintaining immutability. Generic methods and classes allow type parameters to be specified, enabling polymorphism. Static factory methods like ImList<E>.of() create instances without using new.

Uploaded by

Richard Wang
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
13 views

Week 5 Java Generics

Generics allow types to be parameters for classes and interfaces. This allows polymorphism where a generic class like List<E> can bind to any reference type like Integer or Double specified during instantiation. Wrapper classes like Integer box primitive types like int to make them reference types. Auto-boxing and unboxing allow primitive types to be used where reference types are expected. Mutable ArrayList<E> stores elements of type E. Immutable ImList<E> delegates to an encapsulated ArrayList while overriding add() to return a new instance, maintaining immutability. Generic methods and classes allow type parameters to be specified, enabling polymorphism. Static factory methods like ImList<E>.of() create instances without using new.

Uploaded by

Richard Wang
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 6

Week 5: Java generics

What is generics?
Basically, generics is the use of type parameterisation, which is to allow types to be parameters to not only methods, but classes and interfaces (classes and interfaces can
have parameters too!)

The types in this sense ONLY applies to reference types but not primitive types.

Primitive types: int , double etc

Reference types can be:

Boxed primitive types / Wrapper types (eg. Integer , Double )

Classes (eg. Point , Circle )

The basis of generics is still polymorphism, but in a different form.

Normally, polymorphism is displayed when an object from a parent class, without knowing which classes it would reference, is able to invoke method behaviours
according to whichever class (subclass) that it would eventually be referencing.

In generics, polymorphism is displayed when a generic class like List<E> can readily bind to any reference types like Integer , Double that is specified upon instantiation
through type parameterisation. (Parametric polymorphism)

Auto-boxing / Unboxing
Wrapper types (eg. Integer , Double ) wraps around its corresponding primitive types (eg. int , double ) to make it a reference type.

In java, we can do this operation: Integer x = 1. Even though x is a reference type and 1 is a primitive type, java has an auto-boxing feature which boxes the primitive 1 of
type int into an object of its wrapper type, Integer . Thus the assignment is possible.

Operations like list.add(1) also has auto-boxing features that will box the primitive 1 into an Integer object with a value of 1.

Auto-unboxing also happens in operations like int x = list.get(0) , when the Integer object returned from list.get(0) is unboxed to the primitive type int and assigned to
x.

Mutable ArrayList<E>
The symbol E is a generic type. Thus, ArrayList<E> refers to an ArrayList that contains elements of a particular type E .

Whenever we create a new instance of an ArrayList , we would have to specify this generic type. Thus, it can be ArrayList<Integer> , ArrayList<String> etc. This action of
specifying the type is called type parameterisation, which refers to the binding of the generic type ( E ) to the specified type (Eg. Integer ) that happens during compilation

Thus, ArrayList<Integer> is a parameterised type.

Immutable delegation pattern


We can make use of the methods defined in ArrayList to create ImList , instead of having to write the entire ImList class from scratch. The only modification we need to do
is to make ImList immutable instead of being mutable like ArrayList .

The mutable ArrayList class is a property that is encapsulated inside the immutable ImList class, in other words, the client will not know the mutable implementation of the
ImList due to the private keyword.

The implementation of ImList is shown below:

Week 5: Java generics 1


Note that for this case, ImList is NOT a generic class as it lacks the generic type <E> , instead it is a normal class which specifically contains elements of Integer type.

All the method implementations of ImList will be delegated to the ArrayList encapsulated within ImList , EXCEPT for the method that triggers state change, which is
the list.add() method. Thus, for all other methods, ImList will call ArrayList 's method implementation.

For the list.add() method, we will need to create a new ImList object that has the exact same elements in order to maintain immutability. Then, we will need to access
the ArrayList property of the new ImList and call ArrayList 's list.add() method on the ArrayList within the new ImList . Then, we will return the new ImList with the
added element, and the original ImList will still remain the unchanged.

We cannot do the following operation List list = new ImList() , this is because ImList is NOT a subclass of List . This is due to the fact that the list.add() exists in
BOTH List and ImList , but the implementation in ImList CANNOT override the implementation in List due to a different return type (method signature) of the
list.add() method.

Generic classes

Week 5: Java generics 2


Notice that the previously defined ImList ONLY takes in a list of elements of type Integer and not other wrapper types. Thus, we will defined ImList as a generic class
ImList<E> so that it can display polymorphism through type parameterisation.

The new generic implementation ImList<E> is shown below (substitute all “Integer” with “<E>”):

All the instances of <E> within the code of the generic class definition ImList<E> will be “bound” to the generic type <E> in the generic class definition, this generic type
will then be “bound” (type parameterised) to the specific reference type that the client actually specifies upon usage.

Note that all the ImList appearances in the code should be changed to ImList<E> instead.

If there is a compilation error when compiling a generic class, we will add the command -xlint:unchecked behind to see the what are the compilation errors:

An important thing to note is that during the class definition of ImList<E> , writing something like this:

Week 5: Java generics 3


class ImList<Integer> {
...
}

class ImList<String> {
...
}

This will STILL enable the code to compile but it is very confusing as the implementer appears to be type parameterising even before the client attempts to type
parameterise. Thus, NEVER do class ImList<Integer> or class ImList<Double> etc, ALWAYS keep it as class ImList<E> .

Generic methods
Generic methods contains all the common elements of a normal method (method name, arguments, body and return type), but it has an additional component which is the
declaration of the type parameter.

This type declaration is necessary as the generic method will return an object of a generic class. This object will need a reference type, specified by the user, to bind to. The
declaration will be done at the beginning of the method. One example is the code below:

<E> ImList<E> of() {


return new ImList<E>();
}

The empty ImList created will be the Object type.

The above code can only create empty immutable lists. To specify elements to be added into the immutable list, we would have to make some changes to the method like
below:

<E> ImList<E> of(E e) {


return new ImList<E>().add(e);
}

jshell> of(1)
==> [1]
jshell> of('one')
==> [one]

Thus, this will allow type parameterisation to occur when a user specifies the reference type of the elements to be added into the immutable list upon calling the method.
(The type of the immutable list will depend on the reference type of the argument supplied to the method).

Static factory methods (Application of generic method)


The previous example is just to show that generic methods do not need to be tied to any object to be called. This is done through the use of the of() method in JShell. Now,
we will create a generic of() method under the generic class definition of ImList<E> , which will function as a static factory method.

Static factory methods are alternatives to creating an object without the use of the new keyword and the constructor. The keyword static means that the method is not
called with respect to any object, but is called with respect to the class itself. This is how we can implement the of() method under the ImList<E> class:

class ImList<E> {
...
static <E> ImList<E> of() { // Called by ImList.<Reference_type>of()
return new ImList<E>();
}

static <E> ImList<E> of(List<E> elems) {


return new ImList<E>(elems);
}
...
}

Take note that the <E> in the of() method is not bound to the the generic type <E> in the generic class definition. Instead, the <E> in ImList<E> in the of() method is
bound to the type declaration <E> at the beginning of the method. This type declaration <E> at the beginning of the method will then be bound to the reference type
specified during the method call itself via type witnessing.

Usually, for a generic class, we will make all the constructor methods private as we want to hide the implementation of the constructor away from the client. Thus, clients
will not be able to use new to instantiate an object, they will use the static factory method instead.

Type witnessing and type inferencing


Type witnessing is the process of explicitly specifying the reference type of the elements upon calling the static factory method. Examples of type witnessing are shown
below:

// Type witnessing
jshell> ImList.<Integer>of(List.of(1, 2, 3))
==> [1, 2, 3]
jshell> ImList.of(List<Integer>of(1, 2, 3))
==> [1, 2, 3]

Week 5: Java generics 4


As opposed to type witnessing where we explicitly specify the reference type of the elements, we can call the static factory method via type inferencing, which involves not
specifying the reference type of elements and making java infer from the the type based on the elements passed in as the arguments:

// Type inferencing
jshell> ImList.of(List.of(1, 2, 3))
==> [1, 2, 3] // ImList<Integer>
jshell> ImList.of(List.of(1, 2.0, 3))
==> [1, 2.0, 3] // ImList<Number>

Thus, ALWAYS type witness and not type infer.

Generics and substitutability


Generics is invariant: This means that any object that is declared with a parameterised generic type MUST reference to an object with the SAME reference type.

Thus, as opposed to substitutability of normal parent and child classes, doing the statement below with generic classes will not make the code compile

ImList<Shape> shapes = ImList<Circle>.of();

The reason why generic classes are invariant is because of something called heap pollution. Heap pollution occurs in mutable reference types like Array . Here is an
example showing how heap pollution occurs with the mutable Array type:

jshell> Circle[] circles = new Circle[]{new Circle(1), new Circle(2)}


jshell> Shape[] shapes = circles
jshell> shapes[0] = new Circle(3)
jshell> shapes
==> Circle[2] { Circle with radius 3, Circle with radius 2 }
jshell> shapes[0] = new Rectangle(1, 2)
==> // Exception!

Using the Array type, we can let an array containing elements of type Shape reference an array containing elements of type Circle .

However, since both Circle[] circles and Shape[] shapes are referencing the same Circle[] array object, when we try to set an element in the array to an object of type
Rectangle , we will notice that the heap space referenced by Circle[] circles , used to store the Circle[] array object, will contain an element of Rectangle class. Thus
this results in heap pollution.

Upper bounded wildcards


Even though generics are invariant, we can still achieve substitutability through the use of a method defined under the ImList<E> class called the addAll() method. This add
addAll() method will basically take in another List object as argument and add all the elements of the other List object to ImList<E> .

Lets look at this below implementation of addAll() method:

Since there is also an addAll() method defined under the ArrayList class, we can call ArrayList 's addAll() method within the ArrayList property of the other ImList<E>

object. However, this code does not allow for an immutable list containing elements of a superclass to call addAll() on another immutable list containing elements of a
subclass. For example:

jshell> ImList.<Integer>of().add(4).addAll(ImList.<Integer>of(List.of(1, 2, 3))


==> [4, 1, 2, 3] // ImList<Integer> adding another ImList<Integer>
jshell> ImList<Shape>.of().add(new Circle(1)).addAll(ImList.<Shape>of(List.of(new Rectangle(1, 2))))
==> [Circle with radius 1, Rectangle 1 x 2] // ImList<Shape> adding another ImList<Shape>
jshell> ImList<Shape>.of().add(new Circle(1)).addAll(ImList.<Rectangle>of(List.of(new Rectangle(1, 2))))
===> // Error! (ImList<Shape> cannot add ImList<Rectangle> !)

To enable a immutable list containing elements of a superclass to add another immutable list containing elements of a subclass, we will have to modify the addAll() method.
Currently, then <E> in ImList<E> others will be bound to the type Shape , thus we will have to change it to the following:

Thus the extends E refers to an upper bounded wildcard, which in this case is upper bounded by the Shape abstract class. This means that the method addAll() can be
called to any immutable list containing elements of the subclass and not of a more generic class ( ImList<Object> cannot be passed into addAll() when ImList<Shape> is

Week 5: Java generics 5


calling the method.)

Lower bounded wildcards

Week 5: Java generics 6

You might also like