Monday, April 20, 2015

Generics


Generics are implemented by erasure because the types List<Integer>, List<String>, and List<List<String>> are all represented at run-time by the same type, List. Erasure is the process which erases type parameters but adds casts. The implicit casts added by the compilation of generics never fail and is a Cast-iron guarantee. It applies only when no unchecked warnings have been issued by the compiler.

Executing new String[size] allocates an array, and stores in that array an indication that its components are of type String. In contrast, executing new ArrayList<String>() allocates a list, but does not store in the list any indication of the type of its elements. Hence java reifies (makes more concrete) array component types but does not reify list element types.

Conversion of a primitive type to the corresponding reference type is called boxing and vice-versa is called unboxing.

The foreach loop is implemented using a Iterator, with hasNext() and next() method to iterate through the collection. The foreach loop can be applied to any object that implements the interface Iterable<E> (in package java.lang), which in turn refers to the interface Iterator<E>

The static method declares a new type variable by writing <T> at the beginning of the method signature. Such method is called generic method. The scope of the type variable T is local within the method.

Variable method arguments e.g. String... always create an array and they should be used only when the argument does not have generic type e.g. T...

Rule: In a call to a generic method, if there are one or more arguments that correspond to a type parameter and they all have the same type then the type parameter may be inferred; if there are no arguments that correspond to the type parameter or the arguments belong to different subtypes of the
intended type then the type parameter must be given explicitly. When a type parameter is passed to a generic method invocation, it appears in angle brackets to the left, just as in the method declaration.
The Java grammar requires that type parameters may appear only in method invocations that use a dotted form.

         List<Integer> ints = Lists.<Integer>toList();
         List<Object> objs = Lists.<Object>toList(1, "two");

In Java, one type is a subtype of another if they are related by an extends or implements clause.

The Substitution Principle: a variable of a given type may be assigned a value of any subtype of that type, and a method with a parameter of a given type may be invoked with an argument of any subtype of that type.

         List<Number> nums = new ArrayList<Number>();
         nums.add(2);     // permitted
         nums.add(3.14);  // also permitted

In both calls, the E in List<E> is taken to be Number. It may seem reasonable to expect that since Integer is a subtype of Number, it follows
that List<Integer> is a subtype of List<Number>. But this is not the case, because the Substitution Principle would rapidly get us into trouble. It is not always safe to assign a value of type List<Integer> to a variable of type List<Number>.

         List<Integer> ints = new ArrayList<Integer>();
         ints.add(1);
         ints.add(2);
         List<Number> nums = ints; // compilation error, 
         //List<Integer> is not a subtype of List<Number> hence substitution Principle does not apply

List<Integer> is not a subtype of List<Number>, nor is List<Number> a subtype of List<Integer>. List<Integer> is a subtype of itself, and List<Integer> is a subtype of Collection<Integer>. Arrays behave differently in this case were Integer[] is a subtype of Number[].

Wildcards with extends: The phrase "? extends E" means elements of any type that is a subtype of E, were the question mark represents the wildcard. Wildcards can also use wildcards when declaring variables.

         List<Integer> ints = new ArrayList<Integer>();
         ints.add(1);
         ints.add(2);
         List<? extends Number> nums = ints;            // List<Integer> is a subtype of List<? extends Number>
         nums.add(3.14); // compile-time error
In general, if a structure contains elements with a type of the form ? extends E, we can get elements out of the structure, but we cannot put elements into the structure.

Wildcards with super: The phrase "? super T" means elements of any type that is a supertype of T.

         public static <T> void copy(List<? super T> dst, List<? extends T> src) { ... }
         List<Object> objs = Arrays.<Object>asList(2, 3.14, "four");
         List<Integer> ints = Arrays.asList(5, 6);
         Collections.<Number>copy(objs, ints);
The type List<Object> is a subtype of List<? super Number> (since Object is a supertype of Number) and ints has type List<Integer>, which is a subtype of List<? extends Number> (since Integer is a subtype of Number).

The Get and Put Principle: use an extends wildcard when you only get values out of a structure, use a super wildcard when you only put values into a structure, and don’t use a wildcard when you both get and put.
         public static <T> void copy(List<? super T> dst, List<? extends T> src) { ... } // reads from src and puts into dst.

Also if an extends wildcard is present, pretty much all we will be able to do is get but not put values of that type; and if a super wildcard is present, pretty much all we will be able to do is put but not get values of that type.

         List<Integer> ints = new ArrayList<Integer>();
         ints.add(1);
         ints.add(2);
         List<? extends Number> nums = ints;
         double dbl = sum(nums); // sum method gets all values and calculates sum.
         nums.add(3.14); // compile-time error 
We cannot put anything into a type declared with an extends wildcard—except for the value null, which belongs to every reference type. Similarly, we cannot get anything out from a type declared with a super wildcard—except for a value of type Object, which is a supertype of every reference type.

In Java, array subtyping is covariant, meaning that type S[] is considered to be a subtype of T[] whenever S is a subtype of T.

         double into the array:
         Integer[] ints = new Integer[] {1,2,3};
         Number[] nums = ints;
         nums[2] = 3.14;          // array store exception, if we "put" super type S in ARRAY of T
         assert Arrays.toString(ints).equals("[1, 2, 3.14]");

When an array is allocated it is tagged with its reified type (a run-time representation of its component type), and every time an array is assigned into, an array store exception is raised if the reified type is not compatible with the assigned value. In contrast, the subtyping relation for generics is invariant, meaning that type List<S> is not considered to be a subtype of List<T>. Hence compile-time error occurs if we "put" super type S in List of type T in above scenario.

Wildcards reintroduce covariant subtyping for generics, in that type List<S> is considered to be a subtype of List<? extends T> when S is a subtype of T. Wildcards also introduce contravariant subtyping for generics, in that type List<S> is considered to be a subtype of List<? super T> when S is a supertype of T (as opposed to a subtype). Arrays do not support contravariant subtyping.
Generics helps to detect problems at compile time rather than run time making code more efficient and less prone to common errors.

Arrays of primitive type are much more efficient since they don’t involve boxing; and assignments into such an array need not check for an array store exception. Even arrays of reference type may be more efficient than collection classes. Collections on other hand are far more flexible than arrays and support many additional operations than arrays.

Wildcard Capture: When a generic method is invoked, the type parameter may be chosen to match the unknown type represented by a wildcard. This is called wildcard capture.

         public static void reverse(List<?> list) { rev(list); }
            private static <T> void rev(List<T> list) {   // Here the type variable T has captured the wildcard
            List<T> tmp = new ArrayList<T>(list);
            for (int i = 0; i < list.size(); i++) {
                list.set(i, tmp.get(list.size()-i-1));
            }
         }

If the compiler prints an error message containing this type, it is referred to as capture of ?.

Restrictions on Wildcards: In a class instance creation expression, if the type is a parameterized type, then none of the type parameters may be wildcards.

         List<?> list = new ArrayList<?>(); // compile-time error
         Map<String, ? extends Number> map = new HashMap<String, ? extends Number>(); // compile-time error

We can create a structure at a precise type, even if we use wildcard types to put values into or get values from the structure as below:

         List<Number> nums = new ArrayList<Number>();
         List<? super Number> sink = nums;
         List<? extends Number> source = nums;
         for (int i=0; i<10; i++) sink.add(i);

Only top-level parameters in instance creation are prohibited from containing wildcards. Nested wildcards are permitted.

         List<List<?>> lists = new ArrayList<List<?>>();  // Legal
         lists.add(Arrays.asList(1,2,3));
         lists.add(Arrays.asList("four","five"));

Instance creation requires the more specific information. Since wildcards are more general (as interfaces), and ordinary types are more specific (as classes), ordinary types are required for instance creation.

Generic Method Calls: If a generic method call includes explicit type parameters, those type parameters must not be wildcards. If an explicit type parameter is passed, it must not be a wildcard, but nested wildcards are permitted.

         List<?> list = Lists.<?>factory(); // compile-time error
         List<List<?>> = Lists.<List<?>>factory(); // ok

When a class instance is created, it invokes the initializer for its supertype. Hence, any restriction that applies to instance creation must also apply to supertypes. In a class declaration, if the supertype or any superinterface has type parameters, these types must not be wildcards.

         class AnyList extends ArrayList<?> {...} // compile-time error
         class NestedList extends ArrayList<List<?>> {...} // ok

Comparable Interface: The interface Comparable<T> contains a method that can be used to compare one object to another. x.equals(y) if and only if x.compareTo(y) == 0. Comparison differs from equality in that is does not accept a null argument. The ordering provided by the Comparable interface is called the natural ordering. Used mostly by SortedSet and SortedMap.

Never return (this.value - parameter.value) as it causes underflow i.e. difference is larger than Integer.MAX_VALUE. Right way to implement Comparable interface.

     class Integer implements Comparable<Integer> {
         ...
         public int compareTo(Integer that) {
            return this.value < that.value ? -1 :
            this.value == that.value ? 0 : 1 ;
         }
         ...
     }

Unlike wildcards, type variables must always be bounded using extends, never super.

Comparator Interface: Comparator interface provide unnatural or additional orderings. It contains two methods:
     interface Comparator<T> {
         public int compare(T o1, T o2);
         public boolean equals(Object obj);
     }
Each enumerated type declaration can be expanded into a corresponding class designed so that it has exactly one instance for each of the enumerated constants, bound to a suitable static final variable. Each class that corresponds to an enumerated type is a subclass of java.lang.Enum.

First line of the declaration for the Enum class:
     public abstract class Enum<E extends Enum<E>> implements Comparable<E>

First line of the declaration for the Season class:
     class Season extends Enum<Season>

The base class Enum defines two fields, a string name and an integer ordinal, that are possessed by every instance of an enumerated type; the fields are final because once they are initialized, their value never changes. The constructor for the class is protected,to ensure that it is used only within subclasses of this class. Each enumeration class makes the constructor private, to ensure that it is used only to create the enumerated constants. The base class defines accessor methods for the name and ordinal fields. The compareTo method just returns the difference of the
ordinals for the two enumerated values. There are two static methods, the values method returns an array of all the constants of the type. It returns a (shallow) clone of the internal array. The valueOf method takes a string and returns the corresponding constant, found by searching the internal array, else throw a IllegalArgumentException.

Generic types can have Multiple Bounds as below:

     public static 
     <S extends Readable & Closeable, T extends Appendable & Closeable>
     void copy(S src, T trg, int size) throws IOException { .... }

When multiple bounds on a type variable appear, they are separated by ampersands. When multiple bounds appear, the first bound is used for erasure.

Bridges: Generics are implemented by erasure and it compiles in almost exactly the same way as the code without generics. In the case of a parameterized interface such as Comparable<T>, this may cause additional methods to be inserted by the compiler; these additional methods are called bridges.
In the nongeneric class implementing Comparable interface, there are two compareTo methods. The first method is to compare an integer with another
integer and the second compares an integer with an arbitrary object, i.e. it casts the object to an integer and calls the first method. The second method is necessary in order to override the compareTo method in the Comparable interface, because overriding occurs only when the method signatures are identical. This second method is called a bridge. In the generic class, a single compareTo method takes an argument of type Integer and the bridge method is generated automatically by the compiler.

Covariant Overriding: In Java 5, a method can override another if the argument types match exactly and the return type of the overriding method is a subtype of the return type of the other method. For example clone() method which returns an object can be overidden using a clone method which returns a Point class.

Declarations
In a generic class, type parameters appear in the header that declares the class, but not in the constructor.
Static members of a class cannot refer to the type parameter of a generic class, and when accessing a static member the class name should not be parameterized. It is not permitted to follow the class name with type parameters when accessing a static member.

Cell<Integer>.getCount(); // compile-time error

In nested classes, if the outer class has type parameters and the inner class is not static, then type parameters of the outer class are visible within the inner class.

Erasure: The erasure of a type is defined as follows: drop all type parameters from parameterized types, and replace any type variable with the erasure of its bound, or with Object if it has no bound, or with the erasure of the leftmost bound if it has multiple bounds.
  • The erasure of List<Integer>, List<List<String>> is List.
  • The erasure of List<Integer>[] is List[].
  • The erasure of int is itself, also erasure of Integer is itself.
  • The erasure of T in the definition of Arrays.asList is Object.
  • When T is bounded by Comparable<? super T>, the erasure of T is Comparable.
Any two distinct methods cannot have signatures with the same erasure. A class cannot overload two methods whose signatures have the same erasure, and a class cannot implement two interfaces that have the same erasure.

Reification means an explicit representation of a type i.e. run-time type information. In Java, arrays reify information about their component types, while generic types do not reify information about their type parameters. Also a type is reifiable if the type is completely represented at run time that is, if erasure does not remove any useful information.

ArrayList<Number> is not reified type as the list of numbers only carries only the raw type. Even if each element has a reified type Number, its not same as parameter type, for example we would not be able to tell whether we had an ArrayList<Integer>, ArrayList<Number>, or ArrayList<Object>; if the list was empty, we would not be able to tell what kind of empty list it was.

A type is reifiable if it is one of the following: primitive type, nonparameterized class or interface type (String, or Runnable), parameterized type with arguments as unbounded wildcards (List<?>), raw type (List),  array whose component type is reifiable (int[][], List<?>[], Number[]).

A type is not reifiable if it is one of the following: type variable (T), parameterized type with actual parameters (List<Number>) and parameterized type with a bound (List<? extends Number>, Comparable<? super String>).

A cast to a type that is not reifiable is flagged with an unchecked warning, while an instanceof test against a type that is not reifiable is always caught as an error. It is illegal to cast a list of objects to a list of strings, so the cast must take place in two steps. First, cast the list of objects into a list of wildcard type which is safe cast. Then, cast the list of wildcard type into a list of strings which generates unchecked warning.

     return (List<String>)(List<?>)objs; // unchecked cast

In a try-catch statement, each catch clause checks whether the thrown exception matches a given type, similar to instanceof test were the type must be reifiable. A class that extends Throwable must not be a parameterized type i.e. ParamException<T> extends Exception is prohibited.

Arrays are covariant and must reify their component types. It is an error to create a new array unless its component type is reifiable. The two main problems mostly encountered are when the type of the array is a type variable (T[]), and when the type of the array is a parameterized type (List<Integer>).

Collection<String> and LinkedList<String> are both parameterized types which are the types that take other types as parameters.

In Java we are unable to create generic arrays and alternative is to use ArrayList or other Collections.

The Principle of Truth in Advertising: the reified type of an array must be a subtype of the erasure of its static type.
The Principle of Indecent Exposure: never publicly expose an array where the components do not have a reifiable type.

The Principle of Truth in Advertising requires that the run-time type of an array is properly reified, and the Principle of Indecent Exposure requires that the compile-time type of an array must be reifiable.

The class Class now takes a type parameter, so Class<T> is the type of the class token for the type T. Class tokens and the getClass method are treated specially by the compiler. If T is a type without type parameters, then T.class has type Class<T>, and if e is an expression of type T then e.getClass( ) has type Class<? extends T>. The wildcard is needed because the type of the object referred to by the variable may be a subtype of the type of the variable.

     Class<Integer> ki = Integer.class;
     Number n = new Integer(42);
     Class<? extends Number> kn = n.getClass();

The two methods getClass method and class literals which produce a class with a type parameter, are both designed to yield a reifiable type for the type parameter in all cases. In general, if expression e has type T, then the expression e.getClass() has type Class<? extends |T|>, where |T| is the erasure of the type T.

     List<Integer> ints = new ArrayList<Integer>();
     Class<? extends List> k = ints.getClass();
     assert k == ArrayList.class;

Class tokens require a raw type; not even unbounded wildcards may appear.

The reflection library provides a Type interface to describe a generic type. The class Class implements while the interfaces ParameterizedType, TypeVariable, GenericArrayType, WildcardType extends the Type interface.


No comments:

Post a Comment