This article will guide you to one of the newest, most powerful (and one of the most controversial) features of Java programming language.

Initial part of this article will cover the fundamentals of Generics in Java including an introduction, boxing & unboxing, foreach loops, and functions with a variable number of arguments. Some more concepts including how subtyping work and much more will be discussed in this paper. Let’s get started !

What are Generics?

An interface or class may be declared to take one or more type parameters, which are written in angle brackets. This type should be supplied when you declare a variable, belonging to an interface or a class or when you create a new instance of a class.

Let’s look at a simple example:

List<String> word = new ArrayList<String>(); word.add("DiscoverSDK : "); word.add("Generics"); String str = word.get(0) + word.get(1); assert str.equals("DiscoverSDK : Generics");

This trivial code sample defines a variable word to contain a list of Strings. Without generics, same code will be written as:

List words = new ArrayList(); words.add("DiscoverSDK : "); words.add("Generics"); String s = ((String) words.get(0)) + ((String) words.get(1)); assert s.equals("DiscoverSDK : Generics");

Without generics, type parameters are omitted but you must explicitly cast whenever you extract an element from your collection

>>> The Guarantee : The implicit casts added by the compilation of generics will never fail.

Generics allows us to customize a "generic" method or class to whatever type we’re working with. For example, suppose we have a method that adds two numbers together. In order to work with the types themselves, we might have to create multiple versions of this method, like this:

public int Add(int a, int b) public double Add(double a, double b) public float Add(float a, float b)

Generics allows us to create a single method that is customized for the type that invokes it, as shown:

public T Add<T>(T a, T b)

T is substituted for whatever type you use.

Generics and Collections

Generics and Collections work fine with many new features introduced in latest versions of Java, including boxing and unboxing, foreach loop and more.

We will begin with an example where we put five numbers in a list and add them. Here is how to do it in Java:

List<Integer> ints = Arrays.asList(3, 6, 9, 12, 15); int sum = 0; for (int num : ints) sum += num; assert sum == 45;

Almost no explanation is required for above code sample but let’s look at the key points. The interface List and the class Arrays are part of the Collections Framework. The List is now generic. We write List<E> to refer to a list containing elements of type E. Here we mentioned List<Integers> to indicate that the items of the list belongs to the class Integer, the wrapper class that corresponds to the primitive type int.

>>> Boxing and unboxing operation used to convert form the primitive type to the wrapper class, are automatically inserted.

Let’s write the same code WITHOUT Generics:

List integers = Arrays.asList(new Integer[]{ new Integer(3), new Integer(6), new Integer(9), new Integer(12), new Integer(15) }); int sums = 0; for (Iterator it = integers.iterator(); it.hasNext(); ) { int n = ((Integer) it.next()).intValue(); sums += n; } assert sums == 45;

Above code was not so easy to read. Without generics, it is the code rather than the compiler responsible to remember the type of elements so we must mention the cast to Integer.

Boxing and Unboxing

Remember that every type in Java is either a reference type or a primitive type. A reference type is any class, instance or an array type.



>>> All reference types are subtypes of class Object.



Any type of reference type can be set to null.

Conversion of primitive type to a reference type is called Boxing and conversion of reference type to the corresponding primitive type is called unboxing.

Java with generics automatically inserts boxing and unboxing operations where appropriate. For example, the sample:

List<Double> doubles = new ArrayList<Double>(); doubles.add(1.2); double a = doubles.get(0);

Is equivalent to the code:

List<Double> doubles1 = new ArrayList<Double>(); doubles1.add(new Double(1.2)); double another = doubles.get(0).doubleValue();

In practice, compiler will arrange for the value of new Double(1.2) to be cached.

WARNING => The ‘==’ operator is defined differently for reference and the primitive types. On primitives, it is defined by equality of values whereas in reference types, it is defined as object identity.

So, it is better to use equals rather than == to compare values of reference types such as Integer or String or Double and so on.

Wildcards in Generics

When you come across code like this:

public interface Foo<E> {} public interface Bar<T> {} public interface Zar<?> {}

Well there's no difference between the first two - they're just using different names for the type parameter (E or T).

The third isn't a valid declaration - ? is used as a wildcard which is used when providing a type argument, e.g. List<?> bar = ... means that bar refers to a list of some type, but we don't know what.

Difference in List <? extends T > and List <? super T >?

<? extends T> and <? super T> are examples of bounded wildcards. An unbounded wildcard looks like <?>, and basically means <? extends Object>. It loosely means the generic can be any type. A bounded wildcard (<? extends T> or <? super T>) places a restriction on the type by saying that it either has to extend a specific type (<? extends T> is known as an upper bound), or has to be an ancestor of a specific type (<? super T> is known as a lower bound).

In convention,

T is meant to be a Type

E is meant to be an Element (List<E>: a list of Elements)

K is Key (in a Map<K,V>)

V is Value (as a return value or mapped value)

They are fully interchangeable (conflicts in the same declaration notwithstanding).

Wildcards vs Type Parameters

Some other difference between using wildcards and type parameters are:

If you have only one parameterized type argument, then you can use wildcard, although type parameter will also work.

Type parameters support multiple bounds, wildcards don't.

Wildcards support both upper and lower bounds, type parameters just support upper bounds. So, if you want to define a method that takes a List of type Integer or it's super class, you can do:

public void print(List<? super Integer> list) // OK

But you can't use type parameter:

public <T super Integer> void print(List<T> list) // Won't compile

Why Use Generics?

Code that uses generics has many benefits over non-generic code:

Stronger type checks at compile time.

A Java compiler applies strong type checking to generic code and issues errors if the code breaks type safpublic void print(List<? super Integer> list) // OK

But you can't use type parameter:

public <T super Integer> void print(List<T> list) // Won't compile

ety. Fixing compile-time errors is easier than fixing runtime errors, which can be difficult to find.

Elimination of casts.

The following code snippet without generics requires casting:

List list = new ArrayList(); list.add("DiscoverSDK"); String sdk = (String) list.get(0);

When re-written to use generics, the code does not require casting:

List<String> list = new ArrayList<String>(); list.add("DiscoverSDK"); String sdk = list.get(0); // no cast

Enabling programmers to implement generic algorithms.

By using generics, programmers can implement generic algorithms that work on collections of different types, can be customized, and are type safe and easier to read.

Generics vs Templates in C++

There is a big difference between Generics and Templates in C++. In C++, we don't have to specify a class or an interface for the generic type. That's why we can create truly generic functions and classes, with the caveat of a looser typing.

template <typename T> T sum(T a, T b) { return a + b; }

The method above adds two objects of the same type, and can be used for any type T that has the "+" operator available.

In Java you have to specify a type if you want to call methods on the objects passed, something like:

<T extends Something> T sum(T a, T b) { return a.add ( b ); }

In C++ generic functions/classes can only be defined in headers, since the compiler generates different functions for different types (that it's invoked with). So the compilation is slower. In Java the compilation doesn't have a major penalty, but Java uses a technique called "erasure" where the generic type is erased at runtime, so at runtime Java is actually calling …

Something sum(Something a, Something b) { return a.add ( b ); }

So generic programming in Java is not really useful, it's only a little syntactic sugar to help with the new foreach construct.

Semantically, Java Generics are defined by erasure where in C++, Templates are defined by expansion. In C++ templates, each instance of a template at a new type is compiled separately. If you use four list of Integers, four list of Strings, there will be four versions of the code - a problem called as code bloat. In Java, no matter how many types of list you use, there is always just a single version of the code, so bloat does not occur.

Conclusion



In this article, we studied about one of the newest, most powerful features of Java programming language, Generics. We also learned what boxing, unboxing is, looked at the wildcards it uses. So, let’s use Generics to make better apps !