This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
Editor: Mike Loukides Production Services: Windfall Software
Indexers: Maurice Naftalin and Philip Wadler Cover Designer: Karen Montgomery
Printing History: October 2006:
First Edition.
Nutshell Handbook, the Nutshell Handbook logo, and the O’Reilly logo are registered trademarks of O’Reilly Media, Inc. Java Generics and Collections, the image of an alligator, and related trade dress are trademarks of O’Reilly Media, Inc. Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in this book, and O’Reilly Media, Inc. was aware of a trademark claim, the designations have been printed in caps or initial caps. While every precaution has been taken in the preparation of this book, the publisher and authors assume no responsibility for errors or omissions, or for damages resulting from the use of the information contained herein.
TM
This book uses RepKover™, a durable and flexible lay-flat binding. ISBN: 978-0-596-52775-4 [M] 1233352047
[2/09]
We dedicate this book to Joyce Naftalin, Lionel Naftalin, Adam Wadler, and Leora Wadler —Maurice Naftalin and Philip Wadler
Subtyping and the Substitution Principle Wildcards with extends Wildcards with super The Get and Put Principle Arrays Wildcards Versus Type Parameters Wildcard Capture Restrictions on Wildcards
Legacy Library with Legacy Client Generic Library with Generic Client Generic Library with Legacy Client Legacy Library with Generic Client 5.4.1 Evolving a Library using Minimal Changes 5.4.2 Evolving a Library using Stubs 5.4.3 Evolving a Library using Wrappers 5.5 Conclusions
Reifiable Types Instance Tests and Casts Exception Handling Array Creation The Principle of Truth in Advertising The Principle of Indecent Exposure How to Define ArrayList Array Creation and Varargs Arrays as a Deprecated Type? Summing Up
Generics for Reflection Reflected Types are Reifiable Types Reflection for Primitive Types A Generic Reflection Library Reflection for Generics Reflecting Generic Types
Iterable and Iterators Implementations Efficiency and the O-Notation Contracts Collections and Thread Safety 11.5.1 Synchronization and the Legacy Collections 11.5.2 JDK 1.2: Synchronized Collections and Fail-Fast Iterators 11.5.3 Concurrent Collections: Java 5 and Beyond
Java now supports generics, the most significant change to the language since the addition of inner classes in Java 1.2—some would say the most significant change to the language ever. Say you wish to process lists. Some may be lists of integers, others lists of strings, and yet others lists of lists of strings. In Java before generics this is simple. You can represent all three by the same class, called List, which has elements of class Object: list of integers
List
list of strings
List
list of lists of strings
List
In order to keep the language simple, you are forced to do some of the work yourself: you must keep track of the fact that you have a list of integers (or strings or lists of strings), and when you extract an element from the list you must cast it from Object back to Integer (or String or List). For instance, the Collections Framework before generics made extensive use of this idiom. Einstein is reputed to have said, “Everything should be as simple as possible but no simpler”. And some might say the approach above is too simple. In Java with generics you may distinguish different types of lists: list of integers
List
list of strings
List<String>
list of lists of strings
List>
Now the compiler keeps track of whether you have a list of integers (or strings or lists of strings), and no explicit cast back to Integer (or String or List<String>) is required. In some ways, this is similar to generics in Ada or templates in C++, but the actual inspiration is parametric polymorphism as found in functional languages such as ML and Haskell. Part I of this book provides a thorough introduction to generics. We discuss the interactions between generics and subtyping, and how to use wildcards and bounds; we xi
describe techniques for evolving your code; we explain subtleties connected with casts and arrays; we treat advanced topics such as the interaction between generics and security, and how to maintain binary compatibility; and we update common design patterns to exploit generics. Much has been written on generics, and their introduction into Java has sparked some controversy. Certainly, the design of generics involves swings and roundabouts: making it easy to evolve code requires that objects not reify run-time information describing generic type parameters, but the absence of this information introduces corner cases into operations such as casting and array creation.We present a balanced treatment of generics, explaining how to exploit their strengths and work around their weaknesses. Part II provides a comprehensive introduction to the Collections Framework. Newton is reputed to have said, “If I have seen farther than others, it is because I stand on the shoulders of giants”. The best programmers live by this motto, building on existing frameworks and reusable code wherever appropriate. The Java Collections Framework provides reusable interfaces and implementations for a number of common collection types, including lists, sets, queues, and maps. There is also a framework for comparing values, which is useful in sorting or building ordered trees. (Of course, not all programmers exploit reuse. As Hamming said of computer scientists, “Instead of standing on each other’s shoulders, we stand on each other’s toes.”) Thanks to generics, code using collections is easier to read and the compiler will catch more type errors. Further, collections provide excellent illustrations of the use of generics. One might say that generics and collections were made for each other, and, indeed, ease of use of collections was one of the main reasons for introducing generics in the first place. Java 5 and 6 not only update the Collections Framework to exploit generics, but also enhance the framework in other ways, introducing interfaces and classes to support concurrency and the new enum types. We believe that these developments mark the beginning of a shift in programming style, with heavier use of the Collections Framework and, in particular, increased use of collections in favor of arrays. In Part II, we describe the entire framework from first principles in order to help you use collections more effectively, flagging the new features of Java 5 and 6 as we present them. Following common terminology, we refer to the successive versions of Java as 1.0 up to 1.4 and then 5 and 6. We say ‘Java before generics’ to refer to Java 1.0 through 1.4, and ‘Java with generics’ to refer to Java 5 and 6. The design of generics for Java is influenced by a number of previous proposals— notably, GJ, by Bracha, Odersky, Stoutamire, and Wadler; the addition of wildcards to GJ, proposed by Igarashi and Viroli; and further development of wildcards, by Torgersen, Hansen, Ernst, von der Ahé, Bracha, and Gafter. Design of generics was carried out under the Java Community Process by a team led by Bracha, and including Odersky, Thorup, and Wadler (as parts of JSR 14 and JSR 201). Odersky’s GJ compiler is the basis of Sun’s current javac compiler. xii | Preface
Obtaining the Example Programs Some of the example programs in this book are available online at: ftp://ftp.oreilly.com/published/oreilly/javagenerics If you can’t get the examples directly over the Internet but can send and receive email, you can use ftpmail to get them. For help using ftpmail, send an email to [email protected] with no subject and the single word “help” in the body of the message.
How to Contact Us You can address comments and questions about this book to the publisher: O’Reilly Media, Inc. 1005 Gravenstein Highway North Sebastopol, CA 95472 (800) 998-9938 (in the United States or Canada) (707) 829-0515 (international/local) (707) 829-0104 (fax) O’Reilly has a web page for this book, which lists errata and any additional information. You can access this page at: http://www.oreilly.com/catalog/javagenerics To comment or ask technical questions about this book, send email to: [email protected] For more information about books, conferences, software, Resource Centers, and the O’Reilly Network, see the O’Reilly web site at: http://www.oreilly.com
Conventions Used in This Book We use the following font and format conventions: • Code is shown in a fixed-width font, with boldface used for emphasis: class Client { public static void main(String[] args) { Stack stack = new ArrayStack(); for (int i = 0; i<4; i++) stack.push(i); assert stack.toString().equals("stack[0, 1, 2, 3]"); } }
Preface | xiii
• We often include code that corresponds to the body of an appropriate main method: Stack stack = new ArrayStack(); for (int i = 0; i<4; i++) stack.push(i); assert stack.toString().equals("stack[0, 1, 2, 3]");
• Code fragments are printed in fixed-width font when they appear within a paragraph (as when we referred to a main method in the preceding item). • We often omit standard imports. Code that uses the Java Collection Framework or other utility classes should be preceded by the line: import java.util.*;
• Sample interactive sessions, showing command-line input and corresponding output, are shown in constant-width font, with user-supplied input preceded by a percent sign: % javac g/Stack.java g/ArrayStack.java g/Stacks.java l/Client.java Note: Client.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details.
• When user-supplied input is two lines long, the first line is ended with a backslash: % javac -Xlint:unchecked g/Stack.java g/ArrayStack.java \ % g/Stacks.java l/Client.java l/Client.java:4: warning: [unchecked] unchecked call to push(E) as a member of the raw type Stack for (int i = 0; i<4; i++) stack.push(new Integer(i));
Using Code Examples This book is here to help you get your job done. In general, you may use the code in this book in your programs and documentation. You do not need to contact us for permission unless you’re reproducing a significant portion of the code. For example, writing a program that uses several chunks of code from this book does not require permission. Selling or distributing a CD-ROM of examples from O’Reilly books does require permission. Answering a question by citing this book and quoting example code does not require permission. Incorporating a significant amount of example code from this book into your product’s documentation does require permission. We appreciate, but do not require, attribution. An attribution usually includes the title, author, publisher, and ISBN. For example: "Java Generics and Collections, by Maurice Naftalin and Philip Wadler. Copyright 2006 O’Reilly Media, Inc., 0-596-52775-6.” If you feel your use of code examples falls outside fair use or the permission given above, feel free to contact us at [email protected].
xiv | Preface
Safari® Books Online When you see a Safari® Books Online icon on the cover of your favorite technology book, that means the book is available online through the O’Reilly Network Safari Bookshelf. Safari offers a solution that’s better than e-books. It’s a virtual library that lets you easily search thousands of top tech books, cut and paste code samples, download chapters, and find quick answers when you need the most accurate, current information. Try it for free at http://safari.oreilly.com.
Acknowledgments The folks at Sun (past and present) were fantastically good about answering our questions. They were always happy to explain a tricky point or mull over a design tradeoff. Thanks to Joshua Bloch, Gilad Bracha, Martin Buchholz, Joseph D. Darcy, Neal M. Gafter, Mark Reinhold, David Stoutamire, Scott Violet, and Peter von der Ahé. It has been a pleasure to work with the following researchers, who contributed to the design of generics for Java: Erik Ernst, Christian Plesner Hansen, Atsushi Igarashi, Martin Odersky, Mads Torgersen, and Mirko Viroli. We received comments and help from a number of people. Thanks to Brian Goetz, David Holmes, Heinz M. Kabutz, Deepti Kalra, Angelika Langer, Stefan Liebeg, Doug Lea, Tim Munro, Steve Murphy, and C K Shibin. We enjoyed reading Heinz M. Kabutz’s The Java Specialists’ Newsletter and Angelika Langer’s Java Generics FAQ, both available online. Our editor, Michael Loukides,was always ready with good advice. Paul C. Anagnostopoulos ofWindfall Software turned our LATEX into camera-ready copy, and JeremyYallop produced the index. Our families kept us sane (and insane). Love to Adam, Ben, Catherine, Daniel, Isaac, Joe, Leora, Lionel, and Ruth.
Preface | xv
PART I
Generics
Generics are a powerful, and sometimes controversial, new feature of the Java programming language. This part of the book describes generics, using the Collections Framework as a source of examples. A comprehensive introduction to the Collections Framework appears in the second part of the book. The first five chapters focus on the fundamentals of generics. Chapter 1 gives an overview of generics and other new features in Java 5, including boxing, foreach loops, and functions with a variable number of arguments. Chapter 2 reviews how subtyping works and explains how wildcards let you use subtyping in connection with generics. Chapter 3 describes how generics work with the Comparable interface, which requires a notion of bounds on type variables. Chapter 4 looks at how generics work with various declarations, including constructors, static members, and nested classes. Chapter 5 explains how to evolve legacy code to exploit generics, and how ease of evolution is a key advantage of the design of generics in Java. Once you have these five chapters under your belt, you will be able to use generics effectively in most basic situations. The next four chapters treat advanced topics. Chapter 6 explains how the same design that leads to ease of evolution also necessarily leads to a few rough edges in the treatment of casts, exceptions, and arrays. The fit between generics and arrays is the worst rough corner of the language, and we formulate two principles to help you work around the problems. Chapter 7 explains new features that relate generics and reflection, including the newly generified type Class and additions to the Java library that support reflection of generic types. Chapter 8 contains advice on how to use generics effectively in practical coding. We consider checked collections, security issues, specialized classes, and binary compatibility. Chapter 9 presents five extended examples, looking at how generics affect five well-known design patterns: Visitor, Interpreter, Function, Strategy, and Subject-Observer.
CHAPTER 1
Introduction
Generics and collections work well with a number of other new features introduced in the latest versions of Java, including boxing and unboxing, a new form of loop, and functions that accept a variable number of arguments. We begin with an example that illustrates all of these. As we shall see, combining them is synergistic: the whole is greater than the sum of its parts. Taking that as our motto, let’s do something simple with sums: put three numbers into a list and add them together. Here is how to do it in Java with generics: List ints = Arrays.asList(1,2,3); int s = 0; for (int n : ints) { s += n; } assert s == 6;
You can probably read this code without much explanation, but let’s touch on the key features. The interface List and the class Arrays are part of the Collections Framework (both are found in the package java.util). The type List is now generic; you write List<E> to indicate a list with elements of type E. Here we write List to indicate that the elements of the list belong to the class Integer, the wrapper class that corresponds to the primitive type int. Boxing and unboxing operations, used to convert from the primitive type to the wrapper class, are automatically inserted. The static method asList takes any number of arguments, places them into an array, and returns a new list backed by the array. The new loop form, foreach, is used to bind a variable successively to each element of the list, and the loop body adds these into the sum. The assertion statement (introduced in Java 1.4), is used to check that the sum is correct; when assertions are enabled, it throws an error if the condition does not evaluate to true. Here is how the same code looks in Java before generics: List ints = Arrays.asList( new Integer[] { new Integer(1), new Integer(2), new Integer(3) } ); int s = 0; for (Iterator it = ints.iterator(); it.hasNext(); ) { int n = ((Integer)it.next()).intValue();
3
s += n; } assert s == 6;
Reading this code is not quite so easy. Without generics, there is no way to indicate in the type declaration what kind of elements you intend to store in the list, so instead of writing List, you write List. Now it is the coder rather than the compiler who is responsible for remembering the type of the list elements, so you must write the cast to (Integer) when extracting elements from the list. Without boxing and unboxing, you must explicitly allocate each object belonging to the wrapper class Integer and use the intValue method to extract the corresponding primitive int. Without functions that accept a variable number of arguments, you must explicitly allocate an array to pass to the asList method. Without the new form of loop, you must explicitly declare an iterator and advance it through the list. By the way, here is how to do the same thing with an array in Java before generics: int[] ints = new int[] { 1,2,3 }; int s = 0; for (int i = 0; i < ints.length; i++) { s += ints[i]; } assert s == 6;
This is slightly longer than the corresponding code that uses generics and collections, is arguably a bit less readable, and is certainly less flexible. Collections let you easily grow or shrink the size of the collection, or switch to a different representation when appropriate, such as a linked list or hash table or ordered tree. The introduction of generics, boxing and unboxing, foreach loops, and varargs in Java marks the first time that using collections is just as simple, perhaps even simpler, than using arrays. Now let’s look at each of these features in a little more detail.
1.1 Generics An interface or class may be declared to take one or more type parameters, which are written in angle brackets and should be supplied when you declare a variable belonging to the interface or class or when you create a new instance of a class. We saw one example in the previous section. Here is another: List<String> words = new ArrayList<String>(); words.add("Hello "); words.add("world!"); String s = words.get(0)+words.get(1); assert s.equals("Hello world!");
In the Collections Framework, class ArrayList<E> implements interface List<E>. This trivial code fragment declares the variable words to contain a list of strings, creates an instance of an ArrayList, adds two strings to the list, and gets them out again. In Java before generics, the same code would be written as follows:
4 | Chapter 1: Introduction
List words = new ArrayList(); words.add("Hello "); words.add("world!"); String s = ((String)words.get(0))+((String)words.get(1)) assert s.equals("Hello world!");
Without generics, the type parameters are omitted, but you must explicitly cast whenever an element is extracted from the list. In fact, the bytecode compiled from the two sources above will be identical. We say that generics are implemented by erasure because the types List, List<String>, and List> are all represented at run-time by the same type, List. We also use erasure to describe the process that converts the first program to the second. The term erasure is a slight misnomer, since the process erases type parameters but adds casts. Generics implicitly perform the same cast that is explicitly performed without generics. If such casts could fail, it might be hard to debug code written with generics. This is why it is reassuring that generics come with the following guarantee: Cast-iron guarantee: the implicit casts added by the compilation of generics never fail. There is also some fine print on this guarantee: it applies only when no unchecked warnings have been issued by the compiler. Later, we will discuss at some length what causes unchecked warnings to be issued and how to minimize their effect. Implementing generics by erasure has a number of important effects. It keeps things simple, in that generics do not add anything fundamentally new. It keeps things small, in that there is exactly one implementation of List, not one version for each type. And it eases evolution, since the same library can be accessed in both nongeneric and generic forms. This last point is worth some elaboration. It means that you don’t get nasty problems due to maintaining two versions of the libraries: a nongeneric legacy version that works with Java 1.4 or earlier, and a generic version that works with Java 5 and 6. At the bytecode level, code that doesn’t use generics looks just like code that does. There is no need to switch to generics all at once—you can evolve your code by updating just one package, class, or method at a time to start using generics. We even explain how you may declare generic types for legacy code. (Of course, the cast-iron guarantee mentioned above holds only if you add generic types that match the legacy code.) Another consequence of implementing generics by erasure is that array types differ in key ways from parameterized types. 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>()
1.1 Generics | 5
allocates a list, but does not store in the list any indication of the type of its elements. In the jargon, we say that Java reifies array component types but does not reify list element types (or other generic types). Later, we will see how this design eases evolution (see Chapter 5) but complicates casts, instance tests, and array creation (see Chapter 6). Generics Versus Templates Generics in Java resemble templates in C++. There are just two important things to bear in mind about the relationship between Java generics and C++ templates: syntax and semantics. The syntax is deliberately similar and the semantics are deliberately different. Syntactically, angle brackets were chosen because they are familiar to C++ users, and because square brackets would be hard to parse. However, there is one difference in syntax. In C++, nested parameters require extra spaces, so you see things like this: List< List<String> >
In Java, no spaces are required, and it’s fine to write this: List>
You may use extra spaces if you prefer, but they’re not required. (In C++, a problem arises because >> without the space denotes the right-shift operator. Java fixes the problem by a trick in the grammar.) Semantically, Java generics are defined by erasure, whereas C++ templates are defined by expansion. In C++ templates, each instance of a template at a new type is compiled separately. If you use a list of integers, a list of strings, and a list of lists of string, there will be three versions of the code. If you use lists of a hundred different types, there will be a hundred versions of the code—a problem known as code bloat. In Java, no matter how many types of lists you use, there is always one version of the code, so bloat does not occur. Expansion may lead to more efficient implementation than erasure, since it offers more opportunities for optimization, particularly for primitive types such as int. For code that is manipulating large amounts of data—for instance, large arrays in scientific computing—this difference may be significant. However, in practice, for most purposes the difference in efficiency is not important, whereas the problems caused by code bloat can be crucial. In C++, you also may instantiate a template with a constant value rather than a type, making it possible to use templates as a sort of “macroprocessor on steroids” that can perform arbitrarily complex computations at compile time. Java generics are deliberately restricted to types, to keep them simple and easy to understand.
1.2 Boxing and Unboxing Recall that every type in Java is either a reference type or a primitive type. A reference type is any class, interface, or array type. All reference types are subtypes of class Object, and any variable of reference type may be set to the value null. As shown in the 6 | Chapter 1: Introduction
following table, there are eight primitive types, and each of these has a corresponding library class of reference type. The library classes are located in the package java.lang. Primitive
Reference
byte
Byte
short
Short
int
Integer
long
Long
float
Float
double
Double
boolean
Boolean
char
Character
Conversion of a primitive type to the corresponding reference type is called boxing and conversion of the reference type to the corresponding primitive type is called unboxing. Java with generics automatically inserts boxing and unboxing coercions where appropriate. If an expression e of type int appears where a value of type Integer is expected, boxing converts it to new Integer(e) (however, it may cache frequently occurring values). If an expression e of type Integer appears where a value of type int is expected, unboxing converts it to the expression e.intValue(). For example, the sequence: List ints = new ArrayList(); ints.add(1); int n = ints.get(0);
is equivalent to the sequence: List ints = new ArrayList(); ints.add(Integer.valueOf(1)); int n = ints.get(0).intValue();
The call Integer.valueOf(1) is similar in effect to the expression new Integer(1), but may cache some values for improved performance, as we explain shortly. Here, again, is the code to find the sum of a list of integers, conveniently packaged as a static method: public static int sum (List ints) { int s = 0; for (int n : ints) { s += n; } return s; }
Why does the argument have type List and not List? Because type parameters must always be bound to reference types, not primitive types. Why does the result have type int and not Integer? Because result types may be either primitive
1.2 Boxing and Unboxing | 7
or reference types, and it is more efficient to use the former than the latter. Unboxing occurs when each Integer in the list ints is bound to the variable n of type int. We could rewrite the method, replacing each occurrence of int with Integer: public static Integer sumInteger(List ints) { Integer s = 0; for (Integer n : ints) { s += n; } return s; }
This code compiles but performs a lot of needless work. Each iteration of the loop unboxes the values in s and n, performs the addition, and boxes up the result again. With Sun’s current compiler, measurements show that this version is about 60 percent slower than the original. Look Out for This! One subtlety of boxing and unboxing is that == is defined differently on primitive and on reference types. On type int, it is defined by equality of values, and on type Integer, it is defined by object identity. So both of the following assertions succeed using Sun’s JVM: List bigs = Arrays.asList(100,200,300); assert sumInteger(bigs) == sum(bigs); assert sumInteger(bigs) != sumInteger(bigs); // not recommended
In the first assertion, unboxing causes values to be compared, so the results are equal. In the second assertion, there is no unboxing, and the two method calls return distinct Integer objects, so the results are unequal even though both Integer objects represent the same value, 600.We recommend that you never use == to compare values of type Integer. Either unbox first, so == compares values of type int, or else use equals to compare values of type Integer. A further subtlety is that boxed values may be cached. Caching is required when boxing an int or short value between–128 and 127, a char value between '\u0000' and '\u007f', a byte, or a boolean; and caching is permitted when boxing other values. Hence, in contrast to our earlier example, we have the following: List smalls = Arrays.asList(1,2,3); assert sumInteger(smalls) == sum(smalls); assert sumInteger(smalls) == sumInteger(smalls); // not recommended
This is because 6 is smaller than 128, so boxing the value 6 always returns exactly the same object. In general, it is not specified whether boxing the same value twice should return identical or distinct objects, so the inequality assertion shown earlier may either fail or succeed depending on the implementation. Even for small values, for which == will compare values of type Integer correctly, we recommend against its use. It is clearer and cleaner to use equals rather than == to compare values of reference type, such as Integer or String.
8 | Chapter 1: Introduction
1.3 Foreach Here, again, is our code that computes the sum of a list of integers. List ints = Arrays.asList(1,2,3); int s = 0; for (int n : ints) { s += n; } assert s == 6;
The loop in the third line is called a foreach loop even though it is written with the keyword for. It is equivalent to the following: for (Iterator it = ints. iterator(); it.hasNext(); ) { int n = it.next(); s += n; }
The emphasized code corresponds to what was written by the user, and the unemphasized code is added in a systematic way by the compiler. It introduces the variable it of type Iterator to iterate over the list ints of type List. In general, the compiler invents a new name that is guaranteed not to clash with any name already in the code. Note that unboxing occurs when the expression it.next() of type Inte ger is assigned to the variable n of type int. The foreach loop can be applied to any object that implements the interface Itera ble<E> (in package java.lang), which in turn refers to the interface Iterator<E> (in package java.util). These define the methods iterator, hasNext, and next, which are used by the translation of the foreach loop (iterators also have a method remove, which is not used by the translation): interface Iterable<E> { public Iterator<E> iterator(); } interface Iterator<E> { public boolean hasNext(); public E next(); public void remove(); }
All collections, sets, and lists in the Collections Framework implement the Itera ble<E> interface; and classes defined by other vendors or users may implement it as well. The foreach loop may also be applied to an array: public static int sumArray(int[] a) { int s = 0; for (int n : a) { s += n; } return s; }
The foreach loop was deliberately kept simple and catches only the most common case. You need to explicitly introduce an iterator if you wish to use the remove method or to
1.3 Foreach | 9
iterate over more than one list in parallel. Here is a method that removes negative elements from a list of doubles: public static void removeNegative(List v) { for (Iterator it = v.iterator(); it.hasNext();) { if (it.next() < 0) it.remove(); } }
Here is a method to compute the dot product of two vectors, represented as lists of doubles, both of the same length. Given two vectors, u1, … , un and v1, … , vn, it computes u1 * v1> + … + un * vn: public static double dot(List u, List v) { if (u.size() != v.size()) throw new IllegalArgumentException("different sizes"); double d = 0; Iterator uIt = u.iterator(); Iterator vIt = v.iterator(); while (uIt.hasNext()) { assert uIt.hasNext() && vIt.hasNext(); d += uIt.next() * vIt.next(); } assert !uIt.hasNext() && !vIt.hasNext(); return d; }
Two iterators, uIt and vIt, advance across the lists u and v in lock step. The loop condition checks only the first iterator, but the assertions confirm that we could have used the second iterator instead, since we previously tested both lists to confirm that they have the same length.
1.4 Generic Methods and Varargs Here is a method that accepts an array of any type and converts it to a list: class Lists { public static List toList(T[] arr) { List list = new ArrayList(); for (T elt : arr) list.add(elt); return list; } }
The static method toList accepts an array of type T[] and returns a list of type List, and does so for any type T. This is indicated by writing at the beginning of the method signature, which declares T as a new type variable. A method which declares a type variable in this way is called a generic method. The scope of the type variable T is local to the method itself; it may appear in the method signature and the method body, but not outside the method. The method may be invoked as follows:
10 | Chapter 1: Introduction
List ints = Lists.toList(new Integer[] { 1, 2, 3 }); List<String> words = Lists.toList(new String[] { "hello", "world" });
In the first line, boxing converts 1, 2, 3 from int to Integer. Packing the arguments into an array is cumbersome. The vararg feature permits a special, more convenient syntax for the case in which the last argument of a method is an array. To use this feature, we replace T[] with T… in the method declaration: class Lists { public static List toList(T... arr) { List list = new ArrayList(); for (T elt : arr) list.add(elt); return list; } }
Now the method may be invoked as follows: List ints = Lists.toList(1, 2, 3); List<String> words = Lists.toList("hello", "world");
This is just shorthand for what we wrote above. At run time, the arguments are packed into an array which is passed to the method, just as previously. Any number of arguments may precede a last vararg argument. Here is a method that accepts a list and adds all the additional arguments to the end of the list: public static void addAll(List list, T... arr) { for (T elt : arr) list.add(elt); }
Whenever a vararg is declared, one may either pass a list of arguments to be implicitly packed into an array, or explicitly pass the array directly. Thus, the preceding method may be invoked as follows: List ints = new ArrayList(); Lists.addAll(ints, 1, 2); Lists.addAll(ints, new Integer[] { 3, 4 }); assert ints.toString().equals("[1, 2, 3, 4]");
We will see later that when we attempt to create an array containing a generic type, we will always receive an unchecked warning. Since varargs always create an array, they should be used only when the argument does not have a generic type (see Section 6.8). In the preceding examples, the type parameter to the generic method is inferred, but it may also be given explicitly, as in the following examples: List ints = Lists.toList(); List