Powered by Blogger.

Bounded Type Parameters in Java Generics

When you want to restrict the types that can be used as a type arguments in a parameterized type. Then you have to use bounded type parameters. Let us consider the following example. See how bounded type parameter is declared. Type parameter "U" followed by "extends" keyword, then followed by its upper bound (java.lang.Number)

BoundedTypeExample.java:

Output:

T: java.lang.Integer
U: java.lang.Integer

Consider the following code sneppet, since our invocation of inspect includes string, the compilation fails and results in error.

BoundedTypeExample integerClazz = new BoundedTypeExample();
integerClazz.setT(new Integer(10));
integerClazz.inspect(new Integer(20));
integerClazz.inspect("some string"); //ERROR::

ERROR:

Exception in thread "main" java.lang.Error: Unresolved compilation problem
Bound mismatch: The generic method inspect(U) of type BoundedTypeIntro is not applicable for the arguments (String). The inferred type String is not a valid substitute for the bounded parameter

Summary:

Bounded type parameters allows you to do the following:

  • Allows you to limit the types that you are using as a type arguments while instantiating a generic type.
  • Also it allows you to invoke methods defined in the bounds.
To give an example for the second point above consider the following sample code:

EvenOddNumber.java:

Output:

Number 10 is :even number

Erasure of type parameters in Generic Method arguments

The Java compiler erases type parameters in generic method arguments. Let us consider the following example to demonstrate two things:
  • To demonstrate erasure of type parameters in generic method arguments
  • To count number of occurrences of an element in an array
IGenericMethod.java:

GenericMethodImpl.java:

GenericMethodTest.java:

Output:
Number of occurences of element one in an array is: 3

Compile the above source code and inspect using javap command as shown in the picture below. See what Java Compiler has done.


Generics Type Erasure, its Effects and Bridge Methods

Its a technique used by the java compiler to do the following:

1) To replace/translate all parameterized type/ Generic type in Java Generics to raw types in bytecode.
2) To preserve type safety by inserting type casts if necessary.
3) To preserve polymorphism in extended generic types by generating bridge methods.

Erasure of Generic Types:

Erasure of Generic types is done by java compiler while compiling the source code. The main reason why we are using generics is to allow the programmer to write generic code with type safety. So programmers are not forced to write generic code. But its good coding practice to follow while coding Java.

GenericTypeErasureTest.java:

In the abovecode we instantiate generic list using the parameterized type as String for generic type. 

Let us compile the above java code and get a binary class file. Then decompile the same using any Java decompiler of your choice as shown in figure below and check what type erasure has done




















Erasure of Generic Methods:

Similarly the Java compiler also erases type parameters in generic method arguments. Inspect using java decompiler or javap tool to see what java compiler has done.
Refer Example: Erasure of type parameters in generic methods

Effects of Type Erasure and Bridge Methods:

Sometimes type erasure results in a situation, the compiler may need to create a synthetic method called bridge method, as a part of type erasure process. And you dont have to worry about those bridge methods if you happen to see such methods in the stack trace. Let us consider the following example to understand the effects of type erasure and bridge methods.

GenericClazz.java:
ExtGenericClazz.java:
ExtGenericClazzTest.java:

Compile  the above source code. Then inspect using java decompiler or you can also use javap tool to inspect what compiler has done as shown in figure below:



Why these bridge methods are generated by compiler?

After type erasure the method signatures in the ExtGenericClazz may not match in such scenarios. To solve this problem and preserve polymorphism (Class extending parameterized class) of generic types after type erasure, the java compiler generates bridge methods to ensure that subtyping when you extend class should work as expected.



Java Generics Parameterized Types and Raw Types

To create parameterized type of GenericClazz 
  • GenericClazz integerClazz = new GenericClass<>();
To create raw type of GenericClazz, the type argument has to be removed
  • GenericClazz rawClazz = new GenericClazz();
Therefore GenericClazz is the raw type of the generic type GenericClazz. However, a non-generic class or interface type is not a raw type.

Raw Types:

The Raw Types bypass generic type checks, and put off the unsafe code to runtime. Therefore, you should avoid using raw types. You can see Raw types in legacy code because lots of API classes (such as the Collections classes) were not generic prior to JDK 5.0.

When you use raw types now in your code, you can see pre-generics behaviour as explained below:

  • For backward compatibility, assigning a parmaterized type to its raw type is allowed.
          GenericClazz integerGClazz1 = new GenericClazz();
          GenericClazz rawClazz1 = integerGClazz1; //OK
  • You will get type safety warning if you assign a raw type to a parameterized type.
          GenericClazz rawClazz2 = new GenericClazz();
          GenericClazz integerGClazz2 = rawClazz2; //Type safety warning: Unchecked conversion.
  • You also will get type safety warning if you use raw type to invoke generic methods.
          GenericClazz stringGClazz = new GenericClazz();
          GenericClazz rawClazz3 = stringGClazz;
          rawClazz3.setT(8); //Type Safety Warning: Unchecked invocation to setT(T)

RawTypeTest.java:

GenericClazz.java:

Refer Type Erasure for more information on how java compiler uses raw types.

Diamond Operator in Java Generics

In JavaSE 7 or later you can replace the type arguments eg., 'String, Integer' with empty set of type arguments "<>". As long as compiler can determine or can do type inference, the type arguments.

This pair of  angle brackets "<>" is called as diamond operator.

For example you can create an instance of IGenericEntry with the following statement:

IGenericEntry entry1 = new GenericEntryImpl<>("Nithi", 30);

instead of

IGenericEntry entry1 = new GenericEntryImpl("Nithi", 30);

Java Generics: Multiple Type Parameters

A generic class can have multiple type parameters. For example, the generic GenericEntryImpl, which implements the generic IGenericEntry interface:

IGenericEntry.java:


GenericEntryImpl.java:


GenericEntryTest.java:


Note:

  • Due to autoboxing it is valid to pass a String and an int to the class.
  • Java Compiler will take care of automatic conversion from an int to Integer.

Output:
Entry 1 -> Key:Nithi, Value:30
Entry 2 -> Key:Hello, Value:World

Uses of Generics in Java

The Java Generics features were added to the Java language from Java 5. Generics add stability to your code so that you can detect most of the bugs during compile time.

Benefits of generics over non-generic code:

1) Stronger type checks at compile time
Java compiler applies strong type checking to generic code creates error if the code violates type safety. So it is always easy to fix compile time errors than runtime errors. 

2) Eliminates type casts 
The following code sneppet without generics requires type casting

List list = new ArrayList();
list.add("hello");
String string = (String) list.get(0);

When we rewrite the above to use generics, we no need to type cast:

List list = new ArrayList();
list.add("hello");
String string = list.get(0);   // no cast

3) Enable programmers to implement generic algorithms that work on collections of different types, can be customized, and are type safe.

Generic Types:
A generic type is a generic class or interface that is parameterized over types.

A simple Non-generic Class:
The problem with the following non-generic class is, there is no way to verify how the class is used during compile time.

NonGenericClazz.java:


A Generic Version of NonGenericClazz class:
The following is the way/technique to create generic version of non generic class:

GenericClazz.java:

Type variable can be any non-primitive type: any class type, any interface type, any array type or even any other type variable. The same way/technique can be used to create generic interfaces.

The most commonly used type parameter names are:
E - Element (used extensively by the Java Collections Framework)
K - Key
N - Number
T - Type
V - Value

Invoking and Instantiating Generic Type:
GenericClazzTest.java:

Output:
get Integer:10

Try: integerClazz.setT("a String"); will result in compilation error. This shows, if generic code violates type safety, it would result in error as shown below:

Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
The method setT(Integer) in the type GenericClazz is not applicable for the arguments (String)

Note:
Understand the terminologies used in Java Generics i) Type Parameter  ii) Type Argument

  • T in GenericClazz is a "type parameter"
  • Integer in GenericClazz is a "type argument"