Saturday, July 11, 2020

Java Generics Type Bounds


Introduction 

This article is an introduction into Java generic type bounds.

Normal Type

Typically in java normal types are covariant. By that I mean a super type reference can point to sub type object. This allows substitution principle to take its shape in our day-to-day coding. I bet you must have seen this code snippet before

Animal animal = new Bird();

Generic Type

Besides normal types we have special types in Java. These Types take other types as input for their existence e.g. List<T>. These types are called Generic Types. List<T> type needs the T parameter to take shape.Where T is called Type parameter and List is Generic type.
We generally use these generic types to hold normal types and apply generic algorithms through them e.g. sorting of a List, map a Optional value to some other value etc. Other generic type examples are Class<T>, Map<K, V>, Stream<T>, Optional<T>  etc.

The substitution principle(that I mentioned earlier for normal types) in Type parameters needs some tweaking to work with Generic types. This means you will get syntax error if you try to do this.

class Holder<T> {...}

Holder<Animal> holder = new Holder<Bird>();

Though Bird is subtype of Animal, but we cannot substitute Holder<Bird> in Holder<Animal> reference. Next I will introduce type bounds which allows us to have substitution principle to work in type parameters as well.

Type Bounds

Type bounds allows us put some extra type constraints on Type parameters. Through type constraints we can limit the allowed types for Type parameters. There are two flavors of it. They are called Upper Bound and Lower Bound. Next I will explain them one by one.

Upper Bound

In upper bound, type constraint fixates the super type. That means type argument should be bounded by the super type and Type parameter can be any sub class of that super type. We use following syntax for upper bound.

MyGenericClass<T extends SuperType> ref;

Animal in below example is type constraint. The holdRef reference can point to any Holder object whose Type parameter is sub type of Animal e.g.

Holder<T extends Animal> holdRef 

holdRef = new Holder<Bird>
holdRef = new Holder<Dog>
 

Lower Bound

It is very similar to Upper bound but the constraint direction is reversed. In lower bound, type constraint fixates the sub class type and type parameter can be any super class of that sub class type. We use the following syntax for it.

MyGenericClass<T super SubType> ref;

In below example Dog is the type constraint. It limits the type parameter to be any super type of Dog. The holdRef reference can point to any Holder object whose Type parameter is super type of Dog e.g.

Holder<T super Dog> holdRef
holdRef = new Holder<Mammal>
holdRef = new Holder<Animal> 

Conclusion

In java we can use Generic types to represent classes that have very general functionality. This functionality is generally type agnostic. E.g. size of a list, it does not matter if List contains the Integer, String, etc. 

Or functionality that have some type limitation but still can be generalized to large number of types. E.g. sort functionality where type needs to implement Comparable interface through which we can know the relative order between two elements. It does not matter if we pass Integer, String or any user defined class like Employee to sort algorithm as long as type implements the comparable interface.     

Type bounds allows us to put constraint on Type parameter of Genetic types.
Upper bound limits the type parameter to be sub type of a constraint type. Lower bound limits the type parameter to be super type of a constraint type. Both prevents nasty runtime exceptions and save the programmer time and show the error at compilation time. 
    




   

No comments:

Post a Comment

Statistics Notes

Statistics Measuring center of dataset : 1. Mean is sensitive to outliers its beneficial to use mean if dataset member are close to eac...