I have enjoyed to the max reading this book and i believe this is compulsory book to read for every java developer.
There are actually couple of articles by the authors regarding this book.
Infact Heinz M. Kabutz provided a book review regarding this book.
Part 1 consists of Generics Introduction and Part 2 consists of Collections introduction.
I wanted to share few important key points from this book to readers.
1) The Substitution Principle tells us that wherever a value of one type is expected, one may provide a value of any subtype of that type:
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.
2) It may be good practice to insert wildcards whenever possible, but how do you decide which wildcard to use? Where should you use extends, where should you use super, and where is it inappropriate to use a wildcard at all?
Fortunately, a simple principle determines which is appropriate.
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.
3) 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.
4)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.
Example:
public static void reverse(List<?> list) { rev(list); }
private static <T> void rev(List<T> list) {
List<T> tmp = new ArrayList<T>(list);
for (int i = 0; i < list.size(); i++) {
list.set(i, tmp.get(list.size()-i-1));
}
}
Here we say that the type variable T has captured the wildcard. This is a generally useful technique when dealing with wildcards, and it is worth knowing.
5) Wildcards may not appear at the top level in class instance creation expressions (new), in explicit type parameters in generic method calls, or in supertypes (extends and implements).
Instance Creation In a class instance creation expression, if the type is a parameterized type, then none of the type parameters may be wildcards. For example, the following are illegal:
List<?> list = new ArrayList<?>(); // compile-time error
Map<String, ? extends Number> map
= new HashMap<String, ? extends Number>(); // compile-time error
This is usually not a hardship. The Get and Put Principle tells us that if a structure contains a wildcard, we should only get values out of it (if it is an extends wildcard) or only put values into it (if it is a super wildcard). For a structure to be useful, we must do both. Therefore, we usually create a structure at a precise type, even if we use wildcard types to put values into or get values from the structure, as in the following example:
5)Generics are implemented by erasure: when you write code with generics, it compiles in almost exactly the same way as the code you would have written without generics. In the case of a parameterized interface such as Comparable
In the below example 2nd method is called a bridge method.
interface Comparable {
public int compareTo(Object o);
}
class Integer implements Comparable {
private final int value;
public Integer(int value) { this.value = value; }
public int compareTo(Object o) {
return compareTo((Integer)o);
}
public int compareTo(Integer i) {
return (value < i.value) ? -1 : (value == i.value) ? 0 : 1;
}
}
Above Example shows the Comparable interface and a simplified version of the Integer class in Java before generics. In the nongeneric interface, the compareTo method takes an argument of type Object. In the nongeneric class, there are two compareTo methods. The first is the naïve method you might expect, to compare an integer with another integer. The second compares an integer with an arbitrary object: 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
6) 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. Here are some examples:
The erasure of List
- > is List.
The erasure of List
The erasure of List is itself, similarly for any raw type (see Section 5.3 for an explanation of raw types).
The erasure of int is itself, similarly for any primitive type.
The erasure of Integer is itself, similarly for any type without type parameters.
7) In Java, we say that a type is reifiable if the type is completely represented at run time that is, if erasure does not remove any useful information. To be precise, a type is reifiable if it is one of the following:
A primitive type
(such as int)
A nonparameterized class or interface type
(such as Number, String, or Runnable)
A parameterized type in which all type arguments are unbounded wildcards
(such as List, ArrayList, or Map)
A raw type
(such as List, ArrayList, or Map)
An array whose component type is reifiable
(such as int[], Number[], List[], List[], or int[][])
A type is not reifiable if it is one of the following:
A type variable
(such as T)
A parameterized type with actual parameters
(such as List
A parameterized type with a bound
(such as List or Comparable)
So the type List is not reifiable, even though it is equivalent to List. Defining reifiable types in this way makes them easy to identify syntactically.
8) Instance tests and casts depend on examining types at run time, and hence depend on reification. For this reason, an instance test against a type that is not reifiable reports an error, and a cast to a type that is not reifiable usually issues a warning.
9) Arrays reify their component types, meaning that they carry run-time information about the type of their components. This reified type information is used in instance tests and casts, and also used to check whether assignments into array components are permitted.
10) Never publicly expose an array where the components do not have a reifiable type.
11) The Principle of Truth in Advertising:
Here is an attempt to convert a collection to an array, this time using an unchecked cast, and with test code added:
import java.util.*;
class Wrong {
public static <T> T[] toArray(Collection<T> c) {
T[] a = (T[])new Object[c.size()]; // unchecked cast
int i=0; for (T x : c) a[i++] = x;
return a;
}
public static void main(String[] args) {
List<String> strings = Arrays.asList("one","two");
String[] a = toArray(strings); // class cast error
}
}
Indeed, running this program gives the following result:
% java Wrong
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object;
at Wrong.main(Wrong.java:11)
Erasure converts the unchecked cast to T() into a cast to Object[], and inserts a cast to String[] on the call to toArray. When run, the first of these casts succeeds. But even though the array contains only strings, its reified type indicates that it is an array of Object, so the second cast fails.
The Principle of Truth in Advertising: the reified type of an array must be a subtype of the erasure of its static type.
An alternative to using an array to create an array is to use an instance of class Class.
class Right {
public static <T> T[] toArray(Collection<T> c, Class<T> k) {
T[] a = (T[])java.lang.reflect.Array. // unchecked cast
newInstance(k, c.size());
int i=0; for (T x : c) a[i++] = x;
return a;
}
public static void main(String[] args) {
List<String> strings = Arrays.asList("one", "two");
String[] a = toArray(strings, String.class);
assert Arrays.toString(a).equals("[one, two]");
}
}
12) The Principle of Indecent Exposure states that
A library should never publicly expose an array with a nonreifiable type.
public class LibraryDemo {
public static List<Integer>[] intLists(int size) {
List<Integer>[] intLists =
(List<Integer>[]) new List[size]; // unchecked cast
for (int i = 0; i < size; i++)
intLists[i] = Arrays.asList(i+1);
return ints;
}
}
public class Client {
public static void main(String[] args) {
List<Integer>[] intLists = LibraryDemo.intLists(1);
List<? extends Number>[] numLists = intLists;
numLists[0] = Arrays.asList(1.01);
int i = intLists[0].get(0); // class cast error!
}
}
In order to avoid this problem, you must stick to the following principle:
Principle of Indecent Exposure: never publicly expose an array where the components do not have a reifiable type
The Principle of Truth in Advertising and the Principle of Indecent Exposure are closely linked. The first requires that the run-time type of an array is properly reified, and the second requires that the compile-time type of an array must be reifiable.
About the Authors
Maurice Naftalin is Technical Director at Morningside Light Ltd., a software consultancy in the United Kingdom. He has most recently served as an architect and mentor at NSB Retail Systems plc, and as the leader of the client development team of a major U.K. government social service system. He has taught Java since 1998 at both basic and advanced levels for Learning Tree and Sun Educational Services.
Philip Wadler is professor of theoretical computer science at the University of Edinburgh, Scotland, where his research focuses on functional and logic programming. He co-authored the Generic Java standard that became the basis for generics in Sun's Java 5.0, and he also contributed to the XQuery language standard base. He received his Ph.D. in computer science from Carnegie-Mellon University and co-wrote Introduction to Functional Programming (Prentice-Hall).
Hope you enjoy throughly reading this book.
No comments:
Post a Comment