Generics - Java Basic Features


Recently I had to do portlet coding from scratch and refreshed some of JAVA basics and one of that is "Generics" (To get more clarity around difference between Map<String, ?> and Map<String, Object>)

Generics are much like the more familiar formal parameters used in method declarations. The difference is that the inputs to formal parameters are values, while the inputs to type parameters are types

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

Can write as below using the generics

public class Box<T> {
    // T stands for "Type"
    private T t;
    public void set(T t) { this.t = t; }
    public T get() { return t; }
}

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.

You can use any letter (you can use 'X' instead of 'T' in above example) or letter combination , but by convention and to avoid confustion between the actual type , type parameter names are single and uppercase letters. 

E - Element (used extensively by the Java Collections Framework)
K - Key
N - Number
T - Type
V - Value

Type Parameter , eg: public class Box<T> , here 'T' is type parameter
Type Argument, eg: Box<Integer> a=new Box<Integer>(); , here 'Integer' is type argument


Multiple type parameters

Generic classes can also have multiple type parameters ( you can also substitute type parameter with parameterized type)

public class Order<K, V>{
   K key;
   V value;
}
Order<String, Box<String>> boxOder = new Order<String, Box<String>();

NOTE:
In Java SE 7 and later, you can replace the type arguments required to invoke the constructor of a generic class with an empty set of type arguments (<>) as long as the compiler can determine. This pair of angle brackets, <>, is informally called the diamond. For example, you can create an instance of Box<Integer> with the following statement:

Box<Integer> integerBox = new Box<>();

RawType

Assume you have generic class definition as below
public class Box<T> {
    // T stands for "Type"
    private T t;
    public void set(T t) { this.t = t; }
    public T get() { return t; }
}

You can create the object like

Box rawBox = new Box();   //It compiles and runs fine but it gives you java warning. This is just backward compatibility.

You can assign parametric object to rawType reference as below

Box<Integer> box = new Box<>(); //Java7 allows this diamond , by default it considers as Box<Integer>
Box rawBox = box;

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.

class Util{
public static <K,V> boolean compare(Order<K,V> order1, Order<K,V> order2){
return order1.getKey().equals(order2.getKey()) && order1.getValue().equals(order2.getValue());
}
}

//Generic Methods
Order<Integer, String> order1 = new Order<Integer, String>(1111,"First Order");
Order<Integer, String> order2 = new Order<Integer, String>(2222,"Second Order");
Util.<Integer, String>compare(order1, order2);

Util.compare(order1, order2);  //you can also ignore/remove the parameterized the type while calling the method.


Bounded Parameters (using extends in generics)

There may be times when you want to restrict the types that can be used as type arguments in a parameterized type. To declare a bounded type parameter, list the type parameter's name, followed by the extends keyword, followed by its upper bound . Extends is used in a general sense to mean either "extends" (as in classes) or "implements" (as in interfaces).

//Generics Extends
List<? extends Number> testList1 = new ArrayList<Number>();
List<? extends Number> testList2 = new ArrayList<Integer>();
List<? extends Number> testList3 = new ArrayList<Double>();

testList2.add(new Integer(10)); //This is still a error

You can't add any object to List<? extends T> because you can't guarantee what kind of List it is really pointing to, so you can't guarantee that the object is allowed in that List. The only "guarantee" is that you can only read from it and you'll get a T or subclass of T.

Multiple Bound Parameter

Type parameter can have multiple bounds. 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. 

<T extends B1, B2, B3>

Generics inheritance

Box<Integer> is not a subtype of Box<Number> even though Integer is a subtype of Number. Given two concrete types A and B (for example, Number and Integer), MyClass<A> has no relationship to MyClass<B>, regardless of whether or not A and B are related




Wildcard

In generic code, the question mark (?), called the wildcard, represents an unknown type. The wildcard can be used in a variety of situations: as the type of a parameter, field, or local variable; sometimes as a return type (though it is better programming practice to be more specific). The wildcard is never used as a type argument for a generic method invocation, a generic class instance creation, or a supertype.

Upper Bound Wildcard
You can use an upper bounded wildcard to relax the restrictions on a variable. Upper bounded wildcard restricts the unknown type to be a specific type or a subtype of that type and is represented using the extends keyword.

For example, say you want to write a method that works on List<Integer>, List<Double>, and List<Number>; you can achieve this by using an upper bounded wildcard.

To declare an upper-bounded wildcard, use the wildcard character ('?'), followed by the extends keyword, followed by its upper bound.
public static void process(List<? extends Foo> list){ }

Unknown Wildcard
The unbounded wildcard type is specified using the wildcard character (?), for example, List<?>. This is called a list of unknown type

public static void printList(List<Object> list) {
    for (Object elem : list)    System.out.println(elem + " ");
}

The goal of printList is to print a list of any type, but it fails to achieve that goal — it prints only a list of Object instances; it cannot print List<Integer>, List<String>, List<Double>, and so on, because they are not subtypes of List<Object>. To write a generic printList method, use List<?> .
For any concrete type A, List<A> is a subtype of List<?>

It's important to note that List<Object> and List<?> are not the same. You can insert an Object, or any subtype of Object, into a List<Object>. But you can only insert null into a List<?>

Difference between Map<String, ?> and Map<String, Object>

An instance of HashMap<String, String> matches Map<String, ?> but not Map<String, Object>

A thing sometimes misunderstood in Java's generics is that List<String> is not a subtype of List<Object>. (But String[] is in fact a subtype of Object[], that's one of the reasons why generics and arrays don't mix well. (arrays in Java are covariant, generics are not, they are invariant)).



Lower bound wildcard
lower bounded wildcard restricts the unknown type to be a specific type or a super type of that type.  A lower bounded wildcard is expressed using the wildcard character ('?'), following by the super keyword, followed by its lower bound: <? super A>.

Say you want to write a method that puts Integer objects into a list. To maximize flexibility, you would like the method to work on List<Integer>, List<Number>, and List<Object> — anything that can hold Integer values.

public static void addNumbers(List<? super Integer> list) {
    for (int i = 1; i <= 10; i++) {
        list.add(i);
    }
}



Resources

No comments:

Post a Comment