1. In Java SE 7 and beyond, you can omit the generic type in the constructor:
ArrayList<String> files = new ArrayList<>();
The omitted type is inferred from the type of the variable.
2. Generic programming falls into three skill levels. At a basic level, you just use generic classes without thinking how and why they work. Then you need to learn enough about Java generics to solve problems systematically rather than through random tinkering. Finally, of course, you may want to implement your own generic classes and methods.
3. As a rule of thumb, only code that traditionally involved lots of casts from very general types (such as Object or the Comparable interface) will benefit from using type parameters.
4. A generic class is a class with one or more type variables. You can define a generic class by introducing a type variable T, enclosed in angle brackets < >, after the class name.
5. It is common practice to use uppercase letters for type variables, and to keep them short. The Java library uses the variable E for the element type of a collection, K and V for key and value types of a table, and T (and the neighboring letters U and S, if necessary) for “any type at all”.
6. You instantiate the generic type by substituting types for the type variables, such as Pair<String>. In other words, the generic class acts as a factory for ordinary classes.
7.
class ArrayAlg { public static <T> T getMiddle(T... a) { return a[a.length / 2]; } }
It is a generic method, as you can see from the angle brackets and the type variable. Note that the type variables are inserted after the modifiers (public static, in our case) and before the return type. You can define generic methods both inside ordinary classes and inside generic classes.
8. When you call a generic method, you can place the actual types, enclosed in angle brackets, before the method name:
String middle = ArrayAlg.<String>getMiddle("John", "Q.", "Public");
9. The compiler can infer the actual types of the type parameter by matching return type and method parameters.
10. In almost all cases, type inference for generic methods works smoothly. Occasionally, the compiler gets it wrong, and you’ll need to decipher an error report:
double middle = ArrayAlg.getMiddle(3.14, 1729, 0);
The error message complains, in cryptic terms that vary from one compiler version to another, that there are two ways of interpreting this code, both equally valid. In a nutshell, the compiler autoboxed the parameters into a Double and two Integer objects, and then it tried to find a common supertype of these classes. It actually found two: Number and the Comparable interface, which is itself a generic type.
11. The notation <T extends BoundingType> expresses that T should be a subtype of the bounding type. Both T and the bounding type can be either a class or an interface.
12. A type variable or wildcard can have multiple bounds. The bounding types are separated by ampersands (&) because commas are used to separate type variables: T extends Comparable & Serializable. You can have as many interface supertypes as you like, but at most one of the bounds can be a class. If you have a class as a bound, it must be the first one in the bounds list.
13. The virtual machine does not have objects of generic types—all objects belong to ordinary classes. Whenever you define a generic type, a corresponding raw type is automatically provided. The name of the raw type is simply the name of the generic type, with the type parameters removed. The type variables are erased and replaced by their bounding types (or Object for variables without bounds.)
14. The raw type replaces type variables with the first bound, or Object if no bounds are given. The compiler inserts casts to other bound types when necessary. For efficiency, you should therefore put tagging interfaces (that is, interfaces without methods) at the end of the bounds list.
15. When you program a call to a generic method, the compiler inserts casts when the return type has been erased. Casts are also inserted when you access a generic public field.
16. Erasure of methods brings up a couple of complexities. Consider this example:
class DateInterval extends Pair<Date> { public void setSecond(Date second) { if (second.compareTo(getFirst()) >= 0) super.setSecond(second); } . . . }
we’ll want to override the methods to ensure that the second value is never smaller than the first. This class is erased to
class DateInterval extends Pair // after erasure { public void setSecond(Date second) { . . . } . . . }
There is another setSecond method, inherited from Pair, namely,
public void setSecond(Object second)
This is clearly a different method because it has a parameter of a different type—Object instead of Date. So it’s overload instead of override. To fix this problem, the compiler generates a bridge method in the DateInterval class:
public void setSecond(Object second) { setSecond((Date) second); }
17. Bridge methods are not limited to generic types. It is legal for a method to specify a more restrictive return type when overriding another method. For example:
public class Employee implements Cloneable { public Employee clone() throws CloneNotSupportedException { ... } }
The Object.clone and Employee.clone methods are said to have covariant return types.
Actually, the Employee class has two clone methods:
Employee clone() // defined above Object clone() // synthesized bridge method, overrides Object.clone
The synthesized bridge method calls the newly defined method.
18. The key facts about translation of Java generics:
a) There are no generics in the virtual machine, only ordinary classes and methods.
b) All type parameters are replaced by their bounds.
c) Bridge methods are synthesized to preserve polymorphism.
d) Casts are inserted as necessary to preserve type safety.
19. You can get an object of a raw type from a legacy class. You can assign it to a variable whose type uses generics and Vice Versa. But you will get a warning.
20. You cannot substitute a primitive type for a type parameter. Because after erasure the type variable is replaced with Object (or the first bound), you can’t use it to store double value.
21. Runtime type inquiry only works with raw types. You will get a compiler error (with instanceof) or warning (with casts) when you try to inquire whether an object belongs to a generic type. In the same spirit, the getClass method always returns the raw type.
22. You cannot instantiate arrays of parameterized types. Only the creation of these arrays is outlawed. You can declare a variable of type Pair<String>[]. But you can’t initialize it with a new Pair<String>[10]. (Because when it’s erased to Pair[], you cannot avoid adding Pair<Integer> into it by array store check)
23. You can declare arrays of wildcard types and then cast them:
Pair<String>[] table = (Pair<String>[]) new Pair<?>[10];The result is not safe, the array can store instance of Pair with any actual types.
Commented By Sean : so parameterized type array is of no use ?
24. Consider the method that takes varargs:
public static <T> void addAll(Collection<T> coll, T... ts) { for (T t : ts) coll.add(t); }and the following call:
Collection<Pair<String>> table = ...; Pair<String> pair1 = ...; Pair<String> pair2 = ...; addAll(table, pair1, pair2);In order to call this method, the Java virtual machine must make an array of Pair<String>, but you only get a warning, not an error. As of Java SE 7, you can annotate the addAll method itself with @SafeVarargs. Then this method can be called with generic types. You can use this annotation for any methods that merely read the elements of the parameter array.
25. You can use the @SafeVarargs annotation to defeat the restriction against generic array creation, using this method:
@SafeVarargs static <E> E[] array(E... array) { return array; }Now you can call
Pair<String>[] table = array(pair1, pair2);This seems convenient, but there is a hidden danger.
26. You cannot use type variables in expression such as new T(...), new T[...], or T.class.
27. As a workaround, you can construct generic objects through reflection, by calling the Class.newInstance method. You must design the API so that you are handed a Class object, like this:
public static <T> Pair<T> makePair(Class<T> cl) { try { return new Pair<>(cl.newInstance(), cl.newInstance()) } catch (Exception ex) { return null; } }
28. The Class class is itself generic. For example, String.class is an instance (indeed, the sole instance) of Class<String>.
29. You cannot construct a generic array:
public static <T extends Comparable> T[] minmax(T[] a) { T[] mm = new T[2]; . . . } // ERRORType erasure would cause this method to always construct an array Comparable[2].(Thus, this array can not only store actual type instance but also all the subtype of Comaprable.) Instead you can use reflection and call Array.newInstance:
public static <T extends Comparable> T[] minmax(T... a) { T[] mm = (T[]) Array.newInstance(a.getClass().getComponentType(), 2); . . . }
30. The toArray method of the ArrayList class is not so lucky. It needs to produce a T[] array, but it doesn’t have the component type. Therefore, there are two variants:
Object[] toArray() T[] toArray(T[] result)The second method receives an array parameter. If the array is large enough, it is used. Otherwise, a new array of sufficient size is created, using the component type of result.
31. You cannot reference type variables in static fields or methods. Because static members can only have one copy for each class. After type erasure, Pair<T> is erased to Pair and cannot hold several copy of its static fields for different actual type of T.
32. You can neither throw nor catch objects of a generic class. In fact, it is not even legal for a generic class to extend Throwable.
33. You cannot use a type variable in a catch clause:
public static <T extends Throwable> void doWork(Class<T> t) { try { do work } catch (T e) // ERROR--can't catch type variable { Logger.global.info(...) } }However, it is OK to use type variables in exception specifications. The following method is legal:
public static <T extends Throwable> void doWork(T t) throws T // OK { try { do work } catch (Throwable realCause) { t.initCause(realCause); throw t; } }
34. You have to catch all checked exceptions inside the run method of a thread and wrap them into unchecked exceptions—the run method is declared to throw no checked exceptions. But in the following codes, we don’t wrap. We simply throw the exception, tricking the compiler into believing that it is not a checked exception:
public abstract class Block { public abstract void body() throws Exception; public Thread toThread() { return new Thread() { public void run() { try { body(); } catch (Throwable t) { Block.<RuntimeException>throwAs(t); } } }; } @SuppressWarnings("unchecked") public static <T extends Throwable> void throwAs(Throwable e) throws T { throw (T) e; } }Commented by Sean: in this example, if you just cast the Throwable to RuntimeException explicitly , you still can bypass the restriction.
35. The restriction that a class or type variable may not at the same time be a subtype of two interface types which are different parameterizations of the same interface is imposed:
class Calendar implements Comparable<Calendar> { . . . } class GregorianCalendar extends Calendar implements Comparable<GregorianCalendar> { . . . } // ERROR
GregorianCalendar would then implement both Comparable<Calendar> and Comparable<GregorianCalendar>, which are different parameterizations of the same interface. The reason is far more subtle. There would be a conflict with the synthesized bridge methods. A class that implements Comparable<X> gets a bridge method
public int compareTo(Object other) { return compareTo((X) other); }
You cannot have two such methods for different types X.
36. There is no relationship between Pair<S> and Pair<T>, no matter how S and T are related.
37. You can assign a Manager[] array to a variable of type Employee[]:
Manager[] managerBuddies = { ceo, cfo }; Employee[] employeeBuddies = managerBuddies; // OKBut you cannot do this:
ArrayList<Manager> managerBuddies = new ArrayList<Manager>(Arrays.asList(ceo,cfo)); ArrayList<Employee> employeeBuddies = managerBuddies;//ERRORBecause arrays come with special protection. If you try to store a lowly employee into employeeBuddies[0], the virtual machine throws an ArrayStoreException.
38. You can always convert a parameterized type to a raw type. A parameterized type is a subtype of its raw type.
39. Generic classes can extend or implement other generic classes. In this regard, they are no different from ordinary classes. For example, the class ArrayList<T> implements the interface List<T>. That means, an ArrayList<Manager> can be converted to a List<Manager>. However, an ArrayList<Manager> is not an ArrayList<Employee> or List<Employee>.
40. The wildcard type Pair<? extends Employee> denotes any generic Pair type whose type parameter is a subclass of Employee, such as Pair<Manager>, but not Pair<String>. The type Pair<Manager> is a subtype of Pair<? extends Employee>:
41. For subtype bounds wildcard, you cannot call methods that receive a type variable. i.e.:
void setFirst(T a)If the actual parameter is ? extends Employee, the compiler only knows that it needs some subtype of Employee, but it doesn’t know which type. It refuses to pass any specific type. But for those methods that return a type variable, it’s ok to assign the return value to Employee reference. (Because the actual type should be a subclass of Employee.)
42. You can specify a supertype bound: ? super Manager. This wildcard is restricted to all supertypes of Manager. For method:
void setFirst(T a)The compiler doesn’t know the exact type of the setFirst method and therefore can’t call it with an object of type Employee or Object, but only with type Manager or a subtype such as Executive. (Because instances of any subclass of Manager are the instances of superclass of Manager) There is no guarantee about the type of the returned object for a method that returns a type variable. You can only assign it to an Object.
43. Intuitively speaking, wildcards with supertype bounds let you write to a generic object, while wildcards with subtype bounds let you read from a generic object.
44. For method:
public static <T extends Comparable<T>> T min(T[] a)
it cannot deal with GregorianCalendar, because it’s a subclass of Calendar, and Calendar implements Comparable<Calendar>. Thus, GregorianCalendar implements Comparable<Calendar> but not Comparable<GregorianCalendar>. Supertypes come to the rescue:
public static <T extends Comparable<? super T>> T min(T[] a)
45. You can even use wildcards with no bounds at all. The difference between a raw type and a wildcard type with no bounds is that the latter cannot invoke its method that receives a type variable.
46. The getEnumConstants method of Class<T> returns null if this class is not an enum class.
47. You can use the reflection API to determine that
a) The generic method has a type parameter called T;
b) The type parameter has a subtype bound that is itself a generic type;
c) The bounding type has a wildcard parameter;
d) The wildcard parameter has a supertype bound;
e) The generic method has a generic array parameter.
In other words, you can reconstruct everything about generic classes and methods that their implementors declared. However, you won’t know how the type parameters were resolved for specific objects or method calls.
48. In order to express generic type declarations, use the interface Type in the java.lang.reflect package. The interface has the following subtypes:
a) The Class class, describing concrete types
b) The TypeVariable interface, describing type variables (such as T extends Comparable<? super T>)
c) The WildcardType interface, describing wildcards (such as ? super T)
d) The ParameterizedType interface, describing generic class or interface types (such as Comparable<? super T>)
e) The GenericArrayType interface, describing generic arrays (such as T[])
49. For an exhaustive discussion of everything there is to know about Java generics, turn to Angelika Langer’s excellent list of frequently (and not so frequently) asked questions at http://angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html.
50. The following program prints out the information for a generic class:
public class GenericReflectionTest { public static void main(String[] args) { // read class name from command line args or user input String name; if (args.length > 0) name = args[0]; else { Scanner in = new Scanner(System.in); System.out.println("Enter class name (e.g. java.util.Collections): "); name = in.next(); } try { // print generic info for class and public methods Class<?> cl = Class.forName(name); printClass(cl); for (Method m : cl.getDeclaredMethods()) printMethod(m); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static void printClass(Class<?> cl) { System.out.print(cl); printTypes(cl.getTypeParameters(), "<", ", ", ">", true); Type sc = cl.getGenericSuperclass(); if (sc != null) { System.out.print(" extends "); printType(sc, false); } printTypes(cl.getGenericInterfaces(), " implements ", ", ", "", false); System.out.println(); } public static void printMethod(Method m) { String name = m.getName(); System.out.print(Modifier.toString(m.getModifiers())); System.out.print(" "); printTypes(m.getTypeParameters(), "<", ", ", "> ", true); printType(m.getGenericReturnType(), false); System.out.print(" "); System.out.print(name); System.out.print("("); printTypes(m.getGenericParameterTypes(), "", ", ", "", false); System.out.println(")"); } public static void printTypes(Type[] types, String pre, String sep, String suf, boolean isDefinition) { if (pre.equals(" extends ") && Arrays.equals(types, new Type[] { Object.class })) return; if (types.length > 0) System.out.print(pre); for (int i = 0; i < types.length; i++) { if (i > 0) System.out.print(sep); printType(types[i], isDefinition); } if (types.length > 0) System.out.print(suf); } public static void printType(Type type, boolean isDefinition) { if (type instanceof Class) { Class<?> t = (Class<?>) type; System.out.print(t.getName()); } else if (type instanceof TypeVariable) { TypeVariable<?> t = (TypeVariable<?>) type; System.out.print(t.getName()); if (isDefinition) printTypes(t.getBounds(), " extends ", " & ", "", false); } else if (type instanceof WildcardType) { WildcardType t = (WildcardType) type; System.out.print("?"); printTypes(t.getUpperBounds(), " extends ", " & ", "", false); printTypes(t.getLowerBounds(), " super ", " & ", "", false); } else if (type instanceof ParameterizedType) { ParameterizedType t = (ParameterizedType) type; Type owner = t.getOwnerType(); if (owner != null) { printType(owner, false); System.out.print("."); } printType(t.getRawType(), false); printTypes(t.getActualTypeArguments(), "<", ", ", ">", false); } else if (type instanceof GenericArrayType) { GenericArrayType t = (GenericArrayType) type; System.out.print(""); printType(t.getGenericComponentType(), isDefinition); System.out.print("[]"); } } }
相关推荐
Chapter 12. Generic Programming Chapter 13. Collections Chapter 14. Multithreading Appendix A. Java Keywords Volume I: Streams and Files Networking Database programming XML JNDI and LDAP ...
Chapter 12. Functions . 305 Chapter 13. Exception Handling Chapter 14. Namespaces Chapter 15. Source Files and Programs Part III: Abstraction Mechanisms Chapter 16. Classes Chapter 17. Construction, ...
Chapter 12. Input/Output Reading and Writing Binary Data Reading and Writing Text Traversing Directories Embedding Resources Inter-Process Communication Chapter 13. Databases Connecting ...
Later, you'll see how to combine functional programming with object-oriented programming and find out how to refactor your existing code for easy maintenance. What you will learn Write high-quality ...
This book will help you understand the difference between object-oriented programming and protocol-oriented programming. It will demonstrate how to work with protocol-oriented programming using real ...
Unix网络编程卷1,第三版,英文版。大名顶顶的Richard Stevens所写 ... Chapter 12. IPv4 and IPv6 Interoperability Section 12.1. Introduction Section 12.2. IPv4 Client, IPv6 Server Section ...
Chapter 14 Generic Programming Chapter 15 Inductive and Coinductive Types Part VII Variable Types Chapter 16 System F of Polymorphic Types Chapter 17 Abstract Types Chapter 18 Higher Kinds Part VIII...
Chapter 12. Item-Based Graphics 364 Custom and Interactive Graphics Items 366 Animation and Complex Shapes 383 Summary 393 Exercise 394 Chapter 13. Rich Text and Printing 396 Rich Text Editing ...
Chapter 10Control Flowdescribes how control statements direct the order of statement execution. <br>Chapter 11Generic Typesdescribes generic types: how they are written and used, their power, and ...
written by Aharon Ben-Tal Laurent El Ghaoui Arkadi Nemirovski ... PART I....Chapter 1....Robust Counterparts 3 ...C.10 Chapter 12 525 C.11 Chapter 14 527 Bibliography 531 Index 539
Chapter 12. Server-Side Scripting Section 12.1. "Oh What a Tangled Web We Weave" Section 12.2. What's a Server-Side CGI Script? Section 12.3. Climbing the CGI Learning Curve Section 12.4. The...
The Scheme Programming Language <br>Third Edition <br>R. Kent Dybvig <br>Illustrations by Jean-Pierre Hébert Table of Contents Preface <br>Chapter 1. Introduction Section 1.1...
Chapter 6—Prolog: Programming in Logic 6.1. History and Background 6.1.1. The Early Days 6.1.2. Progress on Prolog 6.1.3. Myths and Problems of Prolog 6.1.4. Prolog Resources 6.1.5. The ...
References Chapter 12. Server Setup Strategies Section 12.1. mod_perl Deployment Overview Section 12.2. Standalone mod_perl-Enabled Apache Server Section 12.3. One Plain and ...