Objectives:

  1. Understand Generics, Type parameter, Type arguments, Generic class/interface, Generic type invocation, Generic type instantiation, Generic type, Parameterized type, Raw type, Bounded type parameter
  2. why you shoudn’t use Raw type?
    [Raw types bypass generic type checks, deferring the catch of unsafe code to runtime. Therefore, you should avoid using raw types]Generics:  http://docs.oracle.com/javase/tutorial/java/generics/index.html

“Generics add stability to your code by making more of your bugs detectable at compile time”

“Generics enable types (classes and interfaces) to be parameters when defining classes, interfaces and methods”

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

  1. Stronger type checks at compile time.
  2. Elimination of casts.
  3. Enabling programmers to implement generic algorithms.1. Stronger type checks at compile time.

[code]// without Generics List list = new ArrayList(); list.add(“hello”); // With Generics List<Integer> list = new ArrayList<Integer>(); list.add(“hello”); // will not compile [/code]

2. Elimination of casts

The following code snippet without generics requires casting:

[code]List list = new ArrayList(); list.add(“hello”); String s = (String) list.get(0);[/code]

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

[code]List list = new ArrayList(); list.add(“hello”); String s = list.get(0); // no cast [/code]

3. 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.


Generic Types

A generic type is a generic class or interface that is parameterized over types

Type variable –  T
Create a "generic type" declaration - public class Box<T>

The type variable, T, can be used anywhere inside the class.

Where is the use? Consider a generic case without generics, you might use ‘Object’ type.

Non-generic Box class

[code]public class Box { private Object object; public void set(Object object) { this.object = object; } public Object get() { return object; } }[/code]

Since its methods accept or return an Object, you are free to pass in whatever you want, provided that it is not one of the primitive types. There is no way to verify, at compile time, how the class is used. One part of the code may place an Integer in the box and expect to get Integers out of it, while another part of the code may mistakenly pass in a String, resulting in a runtime error

Generic Box Class    T is called Type parameter or Type variable

[code]public class Box { // T stands for “Type” private T t; public void set(T t) { this.t = t; } public T get() { return t; } }[/code]

A type variable can be any non-primitive type you specify: any class type, any interface type, any array type, or even another type variable.

So we avoided  runtime errors in this case by using Generics


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
  • S,U,V etc. – 2nd, 3rd, 4th types

Invoking and Instantiating a Generic Type

Generic type invocaton :

[code]Box integerBox;[/code]

  • replaced ‘T’ with some concrete value, such as ‘Integer'

Note: generic type invocation as being similar to an ordinary method invocation, but instead of passing an argument to a method, you are passing a type argument

An invocation of a generic type is generally known as a parameterized type

Note:

Type arguments –  are provided in order to create a parameterized type.
eg:  String in Foo<String> f is a type argument.

Type parameter – The T in Foo<T> is a type parameter or type variable.

Generic Type instantiation:

[code]Box integerBox = new Box();[/code]

In Java SE 7 and later : use diamond operator ‘<>’ ie:

[code]Box integerBox = new Box<>();[/code]

Using a generic type parameter in a method

[code]/** * * Using a type parameter requires that the type parameter be declared. The * Java syntax for that is to put in front of the function. This is * exactly analogous to declaring formal parameter names to a method before * using the names in the method body */ public static void printNames3(List names) { System.out.println(names); }[/code]


Multiple Type Parameters

A generic class can have multiple ‘type parameters’

[code]public interface Pair<k, v=””> { public K getKey(); public V getValue(); } public class OrderedPair<k, v=””> implements Pair<k, v=””> { private K key; private V value; public OrderedPair(K key, V value) { this.key = key; this.value = value; } public K getKey() { return key; } public V getValue() { return value; } }[/code]</k,></k,></k,>

The following statements create two instantiations of the OrderedPair class:

[code]Pair<string, integer=””> p1 = new OrderedPair<string, integer=””>(“Even”, 8); Pair<string, string=””> p2 = new OrderedPair<string, string=””>(“hello”, “world”);[/code]</string,></string,></string,></string,>

Due to autoboxing, it is valid to pass a String and an int to the class.

[Autoboxing Since 1.5 – Autoboxing is the automatic conversion that the Java compiler makes between the primitive types and their corresponding object wrapper classes. For example, converting an int to an Integer, a double to a Double, and so on. If the conversion goes the other way, this is called unboxing.]

[code]List li = new ArrayList<>(); for (int i = 1; i < 50; i += 2) li.add(i); //Autoboxing [/code]

[code]Integer i = new Integer(-8); // Unboxing through method invocation int absVal = absoluteValue(i); // unboxing public static int absoluteValue(int i) { return (i < 0) ? -i : i; }[/code]


Parameterized Types & Raw Types

Parameterized Types:

You can also substitute a type parameter (i.e., K or V) with a parameterized type (i.e., List<String>). For example, using the OrderedPair<K, V> example:   [invocation and instantiation]

[code]OrderedPair<string, <strong=””>Box> p = new OrderedPair<>(“primes”, new Box(…));[/code]</string,>

Note: Type arguments –  are provided in order to create a parameterized type.

[List<String> is called a parameterized type. The String in it is called a type argument]

Raw Types:

A raw type – A generic class or interface without any type arguments

For example, given the generic Box class:

[code]public class Box { public void set(T t) { /* … */ } // … }[/code]

[code]Box // Generic Type Box intBox = new Box<>(); // Parameterized type of Box Box rawBox = new Box(); // Raw type of Box [/code]

Note: A non-generic class or interface type is not a raw type.

Some important hands-on:

When using raw types, you essentially get pre-generics behavior — a Box gives you Objects. For backward compatibility, assigning a parameterized type to its raw type is allowed:

Box<String> stringBox = new Box<>();
Box rawBox = stringBox;               // OK

But if you assign a raw type to a parameterized type, you get a warning:

Box rawBox = new Box();           // rawBox is a raw type of Box<T>
Box<Integer> intBox = rawBox;     // warning: unchecked conversion

You also get a warning if you use a raw type to invoke generic methods defined in the corresponding generic type:

Box<String> stringBox = new Box<>();
Box rawBox = stringBox;
rawBox.set(8);  // warning: unchecked invocation to set(T)

The warning shows that raw types bypass generic type checks, deferring the catch of unsafe code to runtime. Therefore, you should avoid using raw types.

More information on how the Java compiler uses raw type is defined in Type Erasure
[http://docs.oracle.com/javase/tutorial/java/generics/erasure.html]


Generic Methods

Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter’s scope is limited to the method where it is declared. Static and non-static generic methods are allowed, as well as generic class constructors.

me: Generic methods have ‘Generic type parameters’ or ‘Generic class’ as arguments

[code]public class Util { public static <k, v=””> boolean compare(Pair<k, v=””> p1, Pair<k, v=””> p2)</k,></k,></k,> { return p1.getKey().equals(p2.getKey()) && p1.getValue().equals(p2.getValue()); } } public class Pair<k, v=””> { private K key; private V value; public Pair(K key, V value) { this.key = key; this.value = value; } public void setKey(K key) { this.key = key; } public void setValue(V value) { this.value = value; } public K getKey() { return key; } public V getValue() { return value; } }[/code]</k,>

The complete syntax for invoking this method would be:

[code]Pair<integer, string=””> p1 = new Pair<>(1, “apple”); Pair<integer, string=””> p2 = new Pair<>(2, “pear”); boolean same = Util.<integer, string=””></integer,>compare(p1, p2);[/code]</integer,></integer,>

The type has been explicitly provided, as shown in bold. Generally, this can be left out and the compiler will infer the type that is needed:

[code]Pair<integer, string=””> p1 = new Pair<>(1, “apple”); Pair<integer, string=””> p2 = new Pair<>(2, “pear”); boolean same = Util.compare(p1, p2);[/code]</integer,></integer,>

This feature, known as type inference, allows you to invoke a generic method as an ordinary method, without specifying a type between angle brackets.

This topic is further discussed in the section – Type Inference
[http://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html]


We have seen type parameter, type argument, parameterized types, raw types, generic type class and even generic methods.
Now let’s see what is bounded type parameter

Bounded Type Parameter:

There may be times when you want to restrict the types that can be used as type arguments in a parameterized type. For example, a method that operates on numbers might only want to accept instances of Number or its subclasses. This is what bounded type parameters are for.

[code]public class Box { private T t; public void set(T t) { this.t = t; } public T get() { return t; } public extends Number> void inspect(U u){ // type parameter with a single bound System.out.println(“T: ” + t.getClass().getName()); System.out.println(“U: ” + u.getClass().getName()); } public static void main(String[] args) { Box integerBox = new Box(); integerBox.set(new Integer(10)); integerBox.inspect(“some text”); // error: this is still String! } }[/code]

In addition to limiting the types you can use to instantiate a generic type, bounded type parameters allow you to invoke methods defined in the bounds:

[code]public class NaturalNumber { private T n; public NaturalNumber(T n) { this.n = n; } public boolean isEven() { return n.intValue() % 2 == 0; } // … }[/code]

The isEven method invokes the intValue method defined in the Integer class through n.

Multiple Bounds

The preceding example illustrates the use of a type parameter with a single bound, but a type parameter can have multiple bounds:

[code][/code]

A type variable with multiple bounds is a subtype of all the types listed in the bound. If one of the bounds is a class, it must be specified first. For example:

[code]Class A { /* … */ } interface B { /* … */ } interface C { /* … */ } class D { /* … */ }[/code]

If bound A is not specified first, you get a compile-time error:

[code]class D { /* … */ } // compile-time error [/code]

To read more beyond this – http://docs.oracle.com/javase/tutorial/java/generics/boundedTypeParams.html


References:

Reference: http://docs.oracle.com/javase/tutorial/java/generics/why.html