Effective Java, 2nd Edition



字数:0 关键词: Java开发 Java

www.it-ebooks.info Praise for the First Edition “I sure wish I had this book ten years ago. Some might think that I don’t need any Java books, but I need this one.” —James Gosling, fellow and vice president, Sun Microsystems, Inc., and inventor of the Java programming language “An excellent book, crammed with good advice on using the Java program- ming language and object-oriented programming in general.” —Gilad Bracha, distinguished engineer, Cadence Design Systems, and coauthor of The Java™ Language Specification, Third Edition (Addison-Wesley, 2005) “10/10—anyone aspiring to write good Java code that others will appreciate reading and maintaining should be required to own a copy of this book. This is one of those rare books where the information won’t become obsolete with subsequent releases of the JDK library.” —Peter Tran, bartender, JavaRanch.com “The best Java book yet written.... Really great; very readable and eminently useful. I can’t say enough good things about this book. At JavaOne 2001, James Gosling said, ‘Go buy this book!’ I’m glad I did, and I couldn’t agree more.” —Keith Edwards, senior member of research staff, Computer Science Lab at the Palo Alto Research Center (PARC), and author of Core JINI (Prentice Hall, 2000) “This is a truly excellent book done by the guy who designed several of the better recent Java platform APIs (including the Collections API).” —James Clark, technical lead of the XML Working Group during the creation of the XML 1.0 Recommendation; editor of the XPath and XSLT Recommendations www.it-ebooks.info “Great content. Analogous to Scott Meyers’s classic Effective C++. If you know the basics of Java, this has to be your next book.” —Gary K. Evans, OO mentor and consultant, Evanetics, Inc. “Josh Bloch gives great insight into best practices that really can only be discov- ered after years of study and experience.” —Mark Mascolino, software engineer “This is a superb book. It clearly covers many of the language/platform subtleties and trickery you need to learn to become a real Java master.” —Victor Wiewiorowski, vice president development and code quality manager, ValueCommerce Co., Tokyo, Japan “I like books that under-promise in their titles and over-deliver in their contents. This book has 57 items of programming advice that are well chosen. Each item reveals a clear, deep grasp of the language. Each one illustrates in simple, practical terms the limits of programming on intuition alone, or taking the most direct path to a solution without fully understanding what the language offers.” —Michael Ernest, Inkling Research, Inc. “I don’t find many programming books that make me want to read every page— this is one of them.” —Matt Tucker, chief technical officer, Jive Software “Great how-to resource for the experienced developer.” —John Zukowski, author of numerous Java books “I picked this book up two weeks ago and can safely say I learned more about the Java language in three days of reading than I did in three months of study! An excellent book and a welcome addition to my Java library.” —Jane Griscti, I/T advisory specialist www.it-ebooks.info Effective Java™ Second Edition www.it-ebooks.info The Java™ Series Ken Arnold, James Gosling, David Holmes The Java™ Programming Language, Fourth Edition Joshua Bloch Effective Java™ Programming Language Guide Joshua Bloch Effective Java,™ Second Edition Stephanie Bodoff, Dale Green, Kim Haase, Eric Jendrock The J2EE™ Tutorial, Second Edition Mary Campione, Kathy Walrath, Alison Huml The Java™ Tutorial, Third Edition: A Short Course on the Basics Mary Campione, Kathy Walrath, Alison Huml, The Tutorial Team The Java™ Tutorial Continued: The Rest of the JDK™ Patrick Chan The Java™ Developers Almanac 1.4, Volume 1 Patrick Chan The Java™ Developers Almanac 1.4, Volume 2 Patrick Chan, Rosanna Lee The Java™ Class Libraries, Second Edition, Volume 2: java.applet, java.awt, java.beans Patrick Chan, Rosanna Lee, Doug Kramer The Java™ Class Libraries, Second Edition, Volume 1: Supplement for the Java™ 2 Platform, Standard Edition, v1.2 Kirk Chen, Li Gong Programming Open Service Gateways with Java™ Embedded Server Zhiqun Chen Java Card™ Technology for Smart Cards: Architecture and Programmer’s Guide Maydene Fisher, Jon Ellis, Jonathan Bruce JDBC™ API Tutorial and Reference, Third Edition Eric Freeman, Susanne Hupfer, Ken Arnold JavaSpaces™ Principles, Patterns, and Practice Li Gong, Gary Ellison, Mary Dageforde Inside Java™ 2 Platform Security, Second Edition: Architecture, API Design, and Implementation James Gosling, Bill Joy, Guy Steele, Gilad Bracha The Java™ Language Specification, Third Edition Chet Haase, Romain Guy Filthy Rich Clients: Developing Animated and Graphical Effects for Desktop Java™ Applications Mark Hapner, Rich Burridge, Rahul Sharma, Joseph Fialli, Kim Haase Java™ Message Service API Tutorial and Reference: Messaging for the J2EE™ Platform Eric Jendrock, Jennifer Ball The Java™ EE 5 Tutorial, Third Edition Jonni Kanerva The Java™ FAQ Jonathan Knudsen Kicking Butt with MIDP and MSA: Creating Great Mobile Applications David Lambert Smarter Selling: Consultative Selling Strategies to Meet Your Buyer’s Needs Every Time Doug Lea Concurrent Programming in Java™, Second Edition: Design Principles and Patterns Rosanna Lee, Scott Seligman JNDI API Tutorial and Reference: Building Directory- Enabled Java™ Applications Sheng Liang The Java™ Native Interface: Programmer’s Guide and Specification Tim Lindholm, Frank Yellin The Java™ Virtual Machine Specification, Second Edition Roger Riggs, Antero Taivalsaari, Jim Van Peursem, Jyri Huopaniemi, Mark Patel, Aleksi Uotila Programming Wireless Devices with the Java™ 2 Platform, Micro Edition, Second Edition Rahul Sharma, Beth Stearns, Tony Ng J2EE™ Connector Architecture and Enterprise Application Integration Inderjeet Singh, Beth Stearns, Mark Johnson, Enterprise Team Designing Enterprise Applications with the J2EE™ Platform, Second Edition Inderjeet Singh, Sean Brydon, Greg Murray, Vijay Ramachandran, Thierry Violleau, Beth Stearns Designing Web Services with the J2EE™ 1.4 Platform: JAX-RPC, SOAP, and XML Technologies Kathy Walrath, Mary Campione, Alison Huml, Sharon Zakhour The JFC Swing Tutorial, Second Edition: A Guide to Constructing GUIs Steve Wilson, Jeff Kesselman Java™ Platform Performance: Strategies and Tactics Sharon Zakhour, Scott Hommel, Jacob Royal, Isaac Rabinovitch, Tom Risser, Mark Hoeber The Java™ Tutorial, Fourth Edition: A Short Course on the Basics www.it-ebooks.info Effective Java™ Second Edition Joshua Bloch Upper Saddle River, NJ • Boston • Indianapolis • San Francisco New York • Toronto • Montreal London • Munich • Paris • Madrid Capetown • Sydney • Tokyo • Singapore • Mexico City www.it-ebooks.info 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 the publisher was aware of a trademark claim, the designations have been printed with initial capital letters or in all capitals. Sun Microsystems, Inc. has intellectual property rights relating to implementations of the technology described in this pub- lication. In particular, and without limitation, these intellectual property rights may include one or more U.S. patents, for- eign patents, or pending applications. Sun, Sun Microsystems, the Sun logo, J2ME, J2EE, Java Card, and all Sun and Java based trademarks and logos are trade- marks or registered trademarks of Sun Microsystems, Inc., in the United States and other countries. UNIX is a registered trademark in the United States and other countries, exclusively licensed through X/Open Company, Ltd. THIS PUBLICA- TION IS PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUD- ING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. THIS PUBLICATION COULD INCLUDE TECHNICAL INACCURACIES OR TYPOGRAPHICAL ERRORS. CHANGES ARE PERIODICALLY ADDED TO THE INFORMA- TION HEREIN; THESE CHANGES WILL BE INCORPORATED IN NEW EDITIONS OF THE PUBLICATION. SUN MICROSYSTEMS, INC. MAY MAKE IMPROVEMENTS AND/OR CHANGES IN THE PRODUCT(S) AND/OR THE PROGRAM(S) DESCRIBED IN THIS PUBLICATION AT ANY TIME. The author and publisher have taken care in the preparation of this book, but make no expressed or implied warranty of any kind and assume no responsibility for errors or omissions. No liability is assumed for incidental or consequential damages in connection with or arising out of the use of the information or programs contained herein. The publisher offers excellent discounts on this book when ordered in quantity for bulk purchases or special sales, which may include electronic versions and/or custom covers and content particular to your business, training goals, marketing focus, and branding interests. For more information, please contact: U.S. Corporate and Government Sales (800) 382-3419 corpsales@pearsontechgroup.com For sales outside the United States please contact: International Sales international@pearsoned.com Visit us on the Web: informit.com/aw Library of Congress Control Number: 2008926278 Copyright © 2008 Sun Microsystems, Inc. 4150 Network Circle, Santa Clara, California 95054 U.S.A. All rights reserved. Printed in the United States of America. This publication is protected by copyright, and permis- sion must be obtained from the publisher prior to any prohibited reproduction, storage in a retrieval system, or trans- mission in any form or by any means, electronic, mechanical, photocopying, recording, or likewise. For information regarding permissions, write to: Pearson Education, Inc. Rights and Contracts Department 501 Boylston Street, Suite 900 Boston, MA 02116 Fax: (617) 671-3447 ISBN-13: 978-0-321-35668-0 ISBN-10: 0-321-35668-3 Text printed in the United States on recycled paper at Courier in Stoughton, Massachusetts. First printing, May 2008 www.it-ebooks.info To my family: Cindy, Tim, and Matt www.it-ebooks.info This page intentionally left blank www.it-ebooks.info ix Contents Foreword . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiii Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .xv Acknowledgments. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xix 1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1 2 Creating and Destroying Objects. . . . . . . . . . . . . . . . . . .5 Item 1: Consider static factory methods instead of constructors . . . 5 Item 2: Consider a builder when faced with many constructor parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 Item 3: Enforce the singleton property with a private constructor or an enum type . . . . . . . . . . . . . . . . . . . . . . . 17 Item 4: Enforce noninstantiability with a private constructor . . . . 19 Item 5: Avoid creating unnecessary objects . . . . . . . . . . . . . . . . . 20 Item 6: Eliminate obsolete object references. . . . . . . . . . . . . . . . . 24 Item 7: Avoid finalizers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 3 Methods Common to All Objects. . . . . . . . . . . . . . . . . .33 Item 8: Obey the general contract when overriding equals . . . . . 33 Item 9: Always override hashCode when you override equals. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 Item 10: Always override toString . . . . . . . . . . . . . . . . . . . . . . . . 51 Item 11: Override clone judiciously . . . . . . . . . . . . . . . . . . . . . . . . 54 Item 12: Consider implementing Comparable . . . . . . . . . . . . . . . . 62 www.it-ebooks.info CONTENTSx 4 Classes and Interfaces. . . . . . . . . . . . . . . . . . . . . . . . . . .67 Item 13: Minimize the accessibility of classes and members. . . . . . 67 Item 14: In public classes, use accessor methods, not public fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 Item 15: Minimize mutability . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 Item 16: Favor composition over inheritance . . . . . . . . . . . . . . . . . . 81 Item 17: Design and document for inheritance or else prohibit it . . 87 Item 18: Prefer interfaces to abstract classes . . . . . . . . . . . . . . . . . . 93 Item 19: Use interfaces only to define types. . . . . . . . . . . . . . . . . . . 98 Item 20: Prefer class hierarchies to tagged classes . . . . . . . . . . . . . 100 Item 21: Use function objects to represent strategies . . . . . . . . . . . 103 Item 22: Favor static member classes over nonstatic . . . . . . . . . . . 106 5 Generics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .109 Item 23: Don’t use raw types in new code . . . . . . . . . . . . . . . . . . . 109 Item 24: Eliminate unchecked warnings. . . . . . . . . . . . . . . . . . . . . 116 Item 25: Prefer lists to arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 Item 26: Favor generic types. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124 Item 27: Favor generic methods . . . . . . . . . . . . . . . . . . . . . . . . . . . 129 Item 28: Use bounded wildcards to increase API flexibility . . . . . 134 Item 29: Consider typesafe heterogeneous containers . . . . . . . . . . 142 6 Enums and Annotations . . . . . . . . . . . . . . . . . . . . . . . .147 Item 30: Use enums instead of int constants. . . . . . . . . . . . . . . . . 147 Item 31: Use instance fields instead of ordinals . . . . . . . . . . . . . . . 158 Item 32: Use EnumSet instead of bit fields . . . . . . . . . . . . . . . . . . . 159 Item 33: Use EnumMap instead of ordinal indexing. . . . . . . . . . . . . 161 Item 34: Emulate extensible enums with interfaces . . . . . . . . . . . . 165 Item 35: Prefer annotations to naming patterns . . . . . . . . . . . . . . . 169 Item 36: Consistently use the Override annotation. . . . . . . . . . . . 176 Item 37: Use marker interfaces to define types . . . . . . . . . . . . . . . 179 7 Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .181 Item 38: Check parameters for validity . . . . . . . . . . . . . . . . . . . . . 181 Item 39: Make defensive copies when needed . . . . . . . . . . . . . . . . 184 Item 40: Design method signatures carefully . . . . . . . . . . . . . . . . . 189 Item 41: Use overloading judiciously. . . . . . . . . . . . . . . . . . . . . . . 191 www.it-ebooks.info CONTENTS xi Item 42: Use varargs judiciously . . . . . . . . . . . . . . . . . . . . . . . . . . 197 Item 43: Return empty arrays or collections, not nulls . . . . . . . . . 201 Item 44: Write doc comments for all exposed API elements . . . . 203 8 General Programming . . . . . . . . . . . . . . . . . . . . . . . . .209 Item 45: Minimize the scope of local variables. . . . . . . . . . . . . . . 209 Item 46: Prefer for-each loops to traditional for loops . . . . . . . . . 212 Item 47: Know and use the libraries . . . . . . . . . . . . . . . . . . . . . . . 215 Item 48: Avoid float and double if exact answers are required . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218 Item 49: Prefer primitive types to boxed primitives . . . . . . . . . . . 221 Item 50: Avoid strings where other types are more appropriate . . 224 Item 51: Beware the performance of string concatenation . . . . . . 227 Item 52: Refer to objects by their interfaces . . . . . . . . . . . . . . . . . 228 Item 53: Prefer interfaces to reflection . . . . . . . . . . . . . . . . . . . . . 230 Item 54: Use native methods judiciously. . . . . . . . . . . . . . . . . . . . 233 Item 55: Optimize judiciously . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234 Item 56: Adhere to generally accepted naming conventions. . . . . 237 9 Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .241 Item 57: Use exceptions only for exceptional conditions . . . . . . . 241 Item 58: Use checked exceptions for recoverable conditions and runtime exceptions for programming errors. . . . . . . 244 Item 59: Avoid unnecessary use of checked exceptions . . . . . . . . 246 Item 60: Favor the use of standard exceptions. . . . . . . . . . . . . . . . 248 Item 61: Throw exceptions appropriate to the abstraction. . . . . . . 250 Item 62: Document all exceptions thrown by each method. . . . . . 252 Item 63: Include failure-capture information in detail messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254 Item 64: Strive for failure atomicity . . . . . . . . . . . . . . . . . . . . . . . 256 Item 65: Don’t ignore exceptions . . . . . . . . . . . . . . . . . . . . . . . . . 258 10 Concurrency. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .259 Item 66: Synchronize access to shared mutable data. . . . . . . . . . . 259 Item 67: Avoid excessive synchronization . . . . . . . . . . . . . . . . . . 265 Item 68: Prefer executors and tasks to threads. . . . . . . . . . . . . . . . 271 Item 69: Prefer concurrency utilities to wait and notify. . . . . . . 273 www.it-ebooks.info CONTENTSxii Item 70: Document thread safety . . . . . . . . . . . . . . . . . . . . . . . . . . 278 Item 71: Use lazy initialization judiciously . . . . . . . . . . . . . . . . . . 282 Item 72: Don’t depend on the thread scheduler . . . . . . . . . . . . . . . 286 Item 73: Avoid thread groups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288 11 Serialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .289 Item 74: Implement Serializable judiciously. . . . . . . . . . . . . . . 289 Item 75: Consider using a custom serialized form . . . . . . . . . . . . . 295 Item 76: Write readObject methods defensively . . . . . . . . . . . . . 302 Item 77: For instance control, prefer enum types to readResolve . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308 Item 78: Consider serialization proxies instead of serialized instances . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 312 Appendix: Items Corresponding to First Edition . . . . . .317 References . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .321 Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .327 www.it-ebooks.info xiii Foreword IF a colleague were to say to you, “Spouse of me this night today manufactures the unusual meal in a home. You will join?” three things would likely cross your mind: third, that you had been invited to dinner; second, that English was not your col- league’s first language; and first, a good deal of puzzlement. If you have ever studied a second language yourself and then tried to use it outside the classroom, you know that there are three things you must master: how the language is structured (grammar), how to name things you want to talk about (vocabulary), and the customary and effective ways to say everyday things (usage). Too often only the first two are covered in the classroom, and you find native speakers constantly suppressing their laughter as you try to make yourself understood. It is much the same with a programming language. You need to understand the core language: is it algorithmic, functional, object-oriented? You need to know the vocabulary: what data structures, operations, and facilities are provided by the standard libraries? And you need to be familiar with the customary and effective ways to structure your code. Books about programming languages often cover only the first two, or discuss usage only spottily. Maybe that’s because the first two are in some ways easier to write about. Grammar and vocabulary are proper- ties of the language alone, but usage is characteristic of a community that uses it. The Java programming language, for example, is object-oriented with single inheritance and supports an imperative (statement-oriented) coding style within each method. The libraries address graphic display support, networking, distrib- uted computing, and security. But how is the language best put to use in practice? There is another point. Programs, unlike spoken sentences and unlike most books and magazines, are likely to be changed over time. It’s typically not enough to produce code that operates effectively and is readily understood by other per- sons; one must also organize the code so that it is easy to modify. There may be ten ways to write code for some task T. Of those ten ways, seven will be awkward, inefficient, or puzzling. Of the other three, which is most likely to be similar to the code needed for the task T' in next year’s software release? www.it-ebooks.info FOREWORDxiv There are numerous books from which you can learn the grammar of the Java Programming Language, including The Java™ Programming Language by Arnold, Gosling, and Holmes [Arnold05] or The Java™ Language Specification by Gos- ling, Joy, yours truly, and Bracha [JLS]. Likewise, there are dozens of books on the libraries and APIs associated with the Java programming language. This book addresses your third need: customary and effective usage. Joshua Bloch has spent years extending, implementing, and using the Java programming language at Sun Microsystems; he has also read a lot of other people’s code, including mine. Here he offers good advice, systematically organized, on how to structure your code so that it works well, so that other people can understand it, so that future modifications and improvements are less likely to cause headaches— perhaps, even, so that your programs will be pleasant, elegant, and graceful. Guy L. Steele Jr. Burlington, Massachusetts April 2001 www.it-ebooks.info xv Preface Preface to the Second Edition A lot has happened to the Java platform since I wrote the first edition of this book in 2001, and it’s high time for a second edition. The most significant set of changes was the addition of generics, enum types, annotations, autoboxing, and the for-each loop in Java 5. A close second was the addition of the new concurrency library, java.util.concurrent, also released in Java 5. With Gilad Bracha, I had the good fortune to lead the teams that designed the new language features. I also had the good fortune to serve on the team that designed and developed the concurrency library, which was led by Doug Lea. The other big change in the platform is the widespread adoption of modern Integrated Development Environments (IDEs), such as Eclipse, IntelliJ IDEA, and NetBeans, and of static analysis tools, such as FindBugs. While I have not been involved in these efforts, I’ve benefited from them immensely and learned how they affect the Java development experience. In 2004, I moved from Sun to Google, but I’ve continued my involvement in the development of the Java platform over the past four years, contributing to the concurrency and collections APIs through the good offices of Google and the Java Community Process. I’ve also had the pleasure of using the Java platform to develop libraries for use within Google. Now I know what it feels like to be a user. As was the case in 2001 when I wrote the first edition, my primary goal is to share my experience with you so that you can imitate my successes while avoiding my failures. The new material continues to make liberal use of real-world exam- ples from the Java platform libraries. The first edition succeeded beyond my wildest expectations, and I’ve done my best to stay true to its spirit while covering all of the new material that was required to bring the book up to date. It was inevitable that the book would grow, and grow it did, from fifty-seven items to seventy-eight. Not only did I add twenty-three items, but I thoroughly revised all the original material and retired a www.it-ebooks.info PREFACExvi few items whose better days had passed. In the Appendix, you can see how the material in this edition relates to the material in the first edition. In the Preface to the First Edition, I wrote that the Java programming language and its libraries were immensely conducive to quality and productivity, and a joy to work with. The changes in releases 5 and 6 have taken a good thing and made it better. The platform is much bigger now than it was in 2001 and more complex, but once you learn the patterns and idioms for using the new features, they make your programs better and your life easier. I hope this edition captures my contin- ued enthusiasm for the platform and helps make your use of the platform and its new features more effective and enjoyable. San Jose, California April 2008 Preface to the First Edition In 1996 I pulled up stakes and headed west to work for JavaSoft, as it was then known, because it was clear that that was where the action was. In the intervening five years I’ve served as Java platform libraries architect. I’ve designed, imple- mented, and maintained many of the libraries and served as a consultant for many others. Presiding over these libraries as the Java platform matured was a once-in-a- lifetime opportunity. It is no exaggeration to say that I had the privilege to work with some of the great software engineers of our generation. In the process, I learned a lot about the Java programming language—what works, what doesn’t, and how to use the language and its libraries to best effect. This book is my attempt to share my experience with you so that you can imi- tate my successes while avoiding my failures. I borrowed the format from Scott Meyers’s Effective C++ [Meyers98], which consists of fifty items, each convey- ing one specific rule for improving your programs and designs. I found the format to be singularly effective, and I hope you do too. In many cases, I took the liberty of illustrating the items with real-world examples from the Java platform libraries. When describing something that could have been done better, I tried to pick on code that I wrote myself, but occasionally I pick on something written by a colleague. I sincerely apologize if, despite my best efforts, I’ve offended anyone. Negative examples are cited not to cast blame www.it-ebooks.info PREFACE xvii but in the spirit of cooperation, so that all of us can benefit from the experience of those who’ve gone before. While this book is not targeted solely at developers of reusable components, it is inevitably colored by my experience writing such components over the past two decades. I naturally think in terms of exported APIs (Application Programming Interfaces), and I encourage you to do likewise. Even if you aren’t developing reusable components, thinking in these terms tends to improve the quality of the software you write. Furthermore, it’s not uncommon to write a reusable compo- nent without knowing it: You write something useful, share it with your buddy across the hall, and before long you have half a dozen users. At this point, you no longer have the flexibility to change the API at will and are thankful for all the effort that you put into designing the API when you first wrote the software. My focus on API design may seem a bit unnatural to devotees of the new lightweight software development methodologies, such as Extreme Programming [Beck99]. These methodologies emphasize writing the simplest program that could possibly work. If you’re using one of these methodologies, you’ll find that a focus on API design serves you well in the refactoring process. The fundamental goals of refactoring are the improvement of system structure and the avoidance of code duplication. These goals are impossible to achieve in the absence of well- designed APIs for the components of the system. No language is perfect, but some are excellent. I have found the Java programming language and its libraries to be immensely conducive to quality and productivity, and a joy to work with. I hope this book captures my enthusiasm and helps make your use of the language more effective and enjoyable. Cupertino, California April 2001 www.it-ebooks.info This page intentionally left blank www.it-ebooks.info xix Acknowledgments Acknowledgments for the Second Edition I thank the readers of the first edition of this book for giving it such a kind and enthusiastic reception, for taking its ideas to heart, and for letting me know what a positive influence it had on them and their work. I thank the many professors who used the book in their courses, and the many engineering teams that adopted it. I thank the whole team at Addison-Wesley for the their kindness, profession- alism, patience, and grace under pressure. Through it all, my editor Greg Doench remained unflappable: a fine editor and a perfect gentleman. My production man- ager, Julie Nahil, was everything that a production manager should be: diligent, prompt, organized, and friendly. My copy editor, Barbara Wood, was meticulous and tasteful. I have once again been blessed with the best team of reviewers imaginable, and I give my sincerest thanks to each of them. The core team, who reviewed every chapter, consisted of Lexi Baugher, Cindy Bloch, Beth Bottos, Joe Bowbeer, Brian Goetz, Tim Halloran, Brian Kernighan, Rob Konigsberg, Tim Peierls, Bill Pugh, Yoshiki Shibata, Peter Stout, Peter Weinberger, and Frank Yellin. Other reviewers included Pablo Bellver, Dan Bloch, Dan Bornstein, Kevin Bourrillion, Martin Buchholz, Joe Darcy, Neal Gafter, Laurence Gonsalves, Aaron Green- house, Barry Hayes, Peter Jones, Angelika Langer, Doug Lea, Bob Lee, Jeremy Manson, Tom May, Mike McCloskey, Andriy Tereshchenko, and Paul Tyma. Again, these reviewers made numerous suggestions that led to great improve- ments in this book and saved me from many embarrassments. And again, any remaining embarrassments are my responsibility. I give special thanks to Doug Lea and Tim Peierls, who served as sounding boards for many of the ideas in this book. Doug and Tim were unfailingly gener- ous with their time and knowledge. I thank my manager at Google, Prabha Krishna, for her continued support and encouragement. www.it-ebooks.info ACKNOWLEDGMENTSxx Finally, I thank my wife, Cindy Bloch, for encouraging me to write, for read- ing each item in raw form, for helping me with Framemaker, for writing the index, and for putting up with me while I wrote. Acknowledgments for the First Edition I thank Patrick Chan for suggesting that I write this book and for pitching the idea to Lisa Friendly, the series managing editor; Tim Lindholm, the series technical editor; and Mike Hendrickson, executive editor of Addison-Wesley. I thank Lisa, Tim, and Mike for encouraging me to pursue the project and for their superhuman patience and unyielding faith that I would someday write this book. I thank James Gosling and his original team for giving me something great to write about, and I thank the many Java platform engineers who followed in James’s footsteps. In particular, I thank my colleagues in Sun’s Java Platform Tools and Libraries Group for their insights, their encouragement, and their sup- port. The team consists of Andrew Bennett, Joe Darcy, Neal Gafter, Iris Garcia, Konstantin Kladko, Ian Little, Mike McCloskey, and Mark Reinhold. Former members include Zhenghua Li, Bill Maddox, and Naveen Sanjeeva. I thank my manager, Andrew Bennett, and my director, Larry Abrahams, for lending their full and enthusiastic support to this project. I thank Rich Green, the VP of Engineering at Java Software, for providing an environment where engi- neers are free to think creatively and to publish their work. I have been blessed with the best team of reviewers imaginable, and I give my sincerest thanks to each of them: Andrew Bennett, Cindy Bloch, Dan Bloch, Beth Bottos, Joe Bowbeer, Gilad Bracha, Mary Campione, Joe Darcy, David Eckhardt, Joe Fialli, Lisa Friendly, James Gosling, Peter Haggar, David Holmes, Brian Kernighan, Konstantin Kladko, Doug Lea, Zhenghua Li, Tim Lindholm, Mike McCloskey, Tim Peierls, Mark Reinhold, Ken Russell, Bill Shannon, Peter Stout, Phil Wadler, and two anonymous reviewers. They made numerous suggestions that led to great improvements in this book and saved me from many embarrassments. Any remaining embarrassments are my responsibility. Numerous colleagues, inside and outside Sun, participated in technical discussions that improved the quality of this book. Among others, Ben Gomes, Steffen Grarup, Peter Kessler, Richard Roda, John Rose, and David Stoutamire www.it-ebooks.info ACKNOWLEDGMENTS xxi contributed useful insights. A special thanks is due Doug Lea, who served as a sounding board for many of the ideas in this book. Doug has been unfailingly generous with his time and his knowledge. I thank Julie Dinicola, Jacqui Doucette, Mike Hendrickson, Heather Olszyk, Tracy Russ, and the whole team at Addison-Wesley for their support and profes- sionalism. Even under an impossibly tight schedule, they were always friendly and accommodating. I thank Guy Steele for writing the Foreword. I am honored that he chose to participate in this project. Finally, I thank my wife, Cindy Bloch, for encouraging and occasionally threatening me to write this book, for reading each item in its raw form, for help- ing me with Framemaker, for writing the index, and for putting up with me while I wrote. www.it-ebooks.info This page intentionally left blank www.it-ebooks.info 1 CHAPTER 1 Introduction THIS book is designed to help you make the most effective use of the Java™ programming language and its fundamental libraries, java.lang, java.util, and, to a lesser extent, java.util.concurrent and java.io. The book discusses other libraries from time to time, but it does not cover graphical user interface programming, enterprise APIs, or mobile devices. This book consists of seventy-eight items, each of which conveys one rule. The rules capture practices generally held to be beneficial by the best and most experienced programmers. The items are loosely grouped into ten chapters, each concerning one broad aspect of software design. The book is not intended to be read from cover to cover: each item stands on its own, more or less. The items are heavily cross-referenced so you can easily plot your own course through the book. Many new features were added to the platform in Java 5 (release 1.5). Most of the items in this book use these features in some way. The following table shows you where to go for primary coverage of these features: Feature Chapter or Item Generics Chapter 5 Enums Items 30–34 Annotations Items 35–37 For-each loop Item 46 Autoboxing Items 40, 49 Varargs Item 42 Static import Item 19 java.util.concurrent Items 68, 69 www.it-ebooks.info CHAPTER 1 INTRODUCTION2 Most items are illustrated with program examples. A key feature of this book is that it contains code examples illustrating many design patterns and idioms. Where appropriate, they are cross-referenced to the standard reference work in this area [Gamma95]. Many items contain one or more program examples illustrating some practice to be avoided. Such examples, sometimes known as antipatterns, are clearly labeled with a comment such as “// Never do this!” In each case, the item explains why the example is bad and suggests an alternative approach. This book is not for beginners: it assumes that you are already comfortable with the Java programming language. If you are not, consider one of the many fine introductory texts [Arnold05, Sestoft05]. While the book is designed to be acces- sible to anyone with a working knowledge of the language, it should provide food for thought even for advanced programmers. Most of the rules in this book derive from a few fundamental principles. Clar- ity and simplicity are of paramount importance. The user of a module should never be surprised by its behavior. Modules should be as small as possible but no smaller. (As used in this book, the term module refers to any reusable software component, from an individual method to a complex system consisting of multiple packages.) Code should be reused rather than copied. The dependencies between modules should be kept to a minimum. Errors should be detected as soon as possi- ble after they are made, ideally at compile time. While the rules in this book do not apply 100 percent of the time, they do characterize best programming practices in the great majority of cases. You should not slavishly follow these rules, but violate them only occasionally and with good reason. Learning the art of programming, like most other disciplines, consists of first learning the rules and then learning when to break them. For the most part, this book is not about performance. It is about writing pro- grams that are clear, correct, usable, robust, flexible, and maintainable. If you can do that, it’s usually a relatively simple matter to get the performance you need (Item 55). Some items do discuss performance concerns, and a few of these items provide performance numbers. These numbers, which are introduced with the phrase “On my machine,” should be regarded as approximate at best. For what it’s worth, my machine is an aging homebuilt 2.2 GHz dual-core AMD Opteron™ 170 with 2 gigabytes of RAM, running Sun’s 1.6_05 release of the Java SE Development Kit (JDK) atop Microsoft Windows® XP Professional SP2. This JDK has two virtual machines, the Java HotSpot™ Client and Server VMs. Performance numbers were measured on the Server VM. www.it-ebooks.info CHAPTER 1 INTRODUCTION 3 When discussing features of the Java programming language and its libraries, it is sometimes necessary to refer to specific releases. For brevity, this book uses “engineering version numbers” in preference to official release names. This table shows the mapping between release names and engineering version numbers. The examples are reasonably complete, but they favor readability over com- pleteness. They freely use classes from the packages java.util and java.io. In order to compile the examples, you may have to add one or more of these import statements: import java.util.*; import java.util.concurrent.*; import java.io.*; Other boilerplate is similarly omitted. The book’s Web site, http:// java.sun.com/docs/books/effective, contains an expanded version of each example, which you can compile and run. For the most part, this book uses technical terms as they are defined in The Java Language Specification, Third Edition [JLS]. A few terms deserve special mention. The language supports four kinds of types: interfaces (including annota- tions), classes (including enums), arrays, and primitives. The first three are known as reference types. Class instances and arrays are objects; primitive values are not. A class’s members consist of its fields, methods, member classes, and member interfaces. A method’s signature consists of its name and the types of its formal parameters; the signature does not include the method’s return type. This book uses a few terms differently from the The Java Language Specifica- tion. Unlike The Java Language Specification, this book uses inheritance as a syn- onym for subclassing. Instead of using the term inheritance for interfaces, this Official Release Name Engineering Version Number JDK 1.1.x / JRE 1.1.x 1.1 Java 2 Platform, Standard Edition, v 1.2 1.2 Java 2 Platform, Standard Edition, v 1.3 1.3 Java 2 Platform, Standard Edition, v 1.4 1.4 Java 2 Platform, Standard Edition, v 5.0 1.5 Java Platform, Standard Edition 6 1.6 www.it-ebooks.info CHAPTER 1 INTRODUCTION4 book simply states that a class implements an interface or that one interface extends another. To describe the access level that applies when none is specified, this book uses the descriptive term package-private instead of the technically cor- rect term default access [JLS, 6.6.1]. This book uses a few technical terms that are not defined in The Java Lan- guage Specification. The term exported API, or simply API, refers to the classes, interfaces, constructors, members, and serialized forms by which a programmer accesses a class, interface, or package. (The term API, which is short for applica- tion programming interface, is used in preference to the otherwise preferable term interface to avoid confusion with the language construct of that name.) A pro- grammer who writes a program that uses an API is referred to as a user of the API. A class whose implementation uses an API is a client of the API. Classes, interfaces, constructors, members, and serialized forms are collec- tively known as API elements. An exported API consists of the API elements that are accessible outside of the package that defines the API. These are the API ele- ments that any client can use and the author of the API commits to support. Not coincidentally, they are also the elements for which the Javadoc utility generates documentation in its default mode of operation. Loosely speaking, the exported API of a package consists of the public and protected members and constructors of every public class or interface in the package. www.it-ebooks.info 5 CHAPTER 2 Creating and Destroying Objects THIS chapter concerns creating and destroying objects: when and how to create them, when and how to avoid creating them, how to ensure they are destroyed in a timely manner, and how to manage any cleanup actions that must precede their destruction. Item 1: Consider static factory methods instead of constructors The normal way for a class to allow a client to obtain an instance of itself is to pro- vide a public constructor. There is another technique that should be a part of every programmer’s toolkit. A class can provide a public static factory method, which is simply a static method that returns an instance of the class. Here’s a simple exam- ple from Boolean (the boxed primitive class for the primitive type boolean). This method translates a boolean primitive value into a Boolean object reference: public static Boolean valueOf(boolean b) { return b ? Boolean.TRUE : Boolean.FALSE; } Note that a static factory method is not the same as the Factory Method pattern from Design Patterns [Gamma95, p. 107]. The static factory method described in this item has no direct equivalent in Design Patterns. A class can provide its clients with static factory methods instead of, or in addition to, constructors. Providing a static factory method instead of a public constructor has both advantages and disadvantages. One advantage of static factory methods is that, unlike constructors, they have names. If the parameters to a constructor do not, in and of themselves, describe the object being returned, a static factory with a well-chosen name is eas- ier to use and the resulting client code easier to read. For example, the constructor www.it-ebooks.info CHAPTER 2 CREATING AND DESTROYING OBJECTS6 BigInteger(int, int, Random), which returns a BigInteger that is probably prime, would have been better expressed as a static factory method named BigIn- teger.probablePrime. (This method was eventually added in the 1.4 release.) A class can have only a single constructor with a given signature. Program- mers have been known to get around this restriction by providing two constructors whose parameter lists differ only in the order of their parameter types. This is a really bad idea. The user of such an API will never be able to remember which constructor is which and will end up calling the wrong one by mistake. People reading code that uses these constructors will not know what the code does with- out referring to the class documentation. Because they have names, static factory methods don’t share the restriction discussed in the previous paragraph. In cases where a class seems to require multi- ple constructors with the same signature, replace the constructors with static fac- tory methods and carefully chosen names to highlight their differences. A second advantage of static factory methods is that, unlike constructors, they are not required to create a new object each time they’re invoked. This allows immutable classes (Item 15) to use preconstructed instances, or to cache instances as they’re constructed, and dispense them repeatedly to avoid creating unnecessary duplicate objects. The Boolean.valueOf(boolean) method illus- trates this technique: it never creates an object. This technique is similar to the Flyweight pattern [Gamma95, p. 195]. It can greatly improve performance if equivalent objects are requested often, especially if they are expensive to create. The ability of static factory methods to return the same object from repeated invocations allows classes to maintain strict control over what instances exist at any time. Classes that do this are said to be instance-controlled. There are several reasons to write instance-controlled classes. Instance control allows a class to guarantee that it is a singleton (Item 3) or noninstantiable (Item 4). Also, it allows an immutable class (Item 15) to make the guarantee that no two equal instances exist: a.equals(b) if and only if a==b. If a class makes this guarantee, then its cli- ents can use the == operator instead of the equals(Object) method, which may result in improved performance. Enum types (Item 30) provide this guarantee. A third advantage of static factory methods is that, unlike constructors, they can return an object of any subtype of their return type. This gives you great flexibility in choosing the class of the returned object. One application of this flexibility is that an API can return objects without making their classes public. Hiding implementation classes in this fashion leads to a very compact API. This technique lends itself to interface-based frameworks (Item 18), where interfaces provide natural return types for static factory methods. www.it-ebooks.info ITEM 1: CONSIDER STATIC FACTORY METHODS INSTEAD OF CONSTRUCTORS 7 Interfaces can’t have static methods, so by convention, static factory methods for an interface named Type are put in a noninstantiable class (Item 4) named Types. For example, the Java Collections Framework has thirty-two convenience implementations of its collection interfaces, providing unmodifiable collections, synchronized collections, and the like. Nearly all of these implementations are exported via static factory methods in one noninstantiable class (java.util.Col- lections). The classes of the returned objects are all nonpublic. The Collections Framework API is much smaller than it would have been had it exported thirty-two separate public classes, one for each convenience imple- mentation. It is not just the bulk of the API that is reduced, but the conceptual weight. The user knows that the returned object has precisely the API specified by its interface, so there is no need to read additional class documentation for the implementation classes. Furthermore, using such a static factory method requires the client to refer to the returned object by its interface rather than its implementa- tion class, which is generally good practice (Item 52). Not only can the class of an object returned by a public static factory method be nonpublic, but the class can vary from invocation to invocation depending on the values of the parameters to the static factory. Any class that is a subtype of the declared return type is permissible. The class of the returned object can also vary from release to release for enhanced software maintainability and performance. The class java.util.EnumSet (Item 32), introduced in release 1.5, has no public constructors, only static factories. They return one of two implementations, depending on the size of the underlying enum type: if it has sixty-four or fewer elements, as most enum types do, the static factories return a RegularEnumSet instance, which is backed by a single long; if the enum type has sixty-five or more elements, the factories return a JumboEnumSet instance, backed by a long array. The existence of these two implementation classes is invisible to clients. If RegularEnumSet ceased to offer performance advantages for small enum types, it could be eliminated from a future release with no ill effects. Similarly, a future release could add a third or fourth implementation of EnumSet if it proved benefi- cial for performance. Clients neither know nor care about the class of the object they get back from the factory; they care only that it is some subclass of EnumSet. The class of the object returned by a static factory method need not even exist at the time the class containing the method is written. Such flexible static factory methods form the basis of service provider frameworks, such as the Java Database Connectivity API (JDBC). A service provider framework is a system in which multiple service providers implement a service, and the system makes the imple- mentations available to its clients, decoupling them from the implementations. www.it-ebooks.info CHAPTER 2 CREATING AND DESTROYING OBJECTS8 There are three essential components of a service provider framework: a ser- vice interface, which providers implement; a provider registration API, which the system uses to register implementations, giving clients access to them; and a ser- vice access API, which clients use to obtain an instance of the service. The service access API typically allows but does not require the client to specify some criteria for choosing a provider. In the absence of such a specification, the API returns an instance of a default implementation. The service access API is the “flexible static factory” that forms the basis of the service provider framework. An optional fourth component of a service provider framework is a service provider interface, which providers implement to create instances of their service implementation. In the absence of a service provider interface, implementations are registered by class name and instantiated reflectively (Item 53). In the case of JDBC, Connection plays the part of the service interface, DriverManager.reg- isterDriver is the provider registration API, DriverManager.getConnection is the service access API, and Driver is the service provider interface. There are numerous variants of the service provider framework pattern. For example, the service access API can return a richer service interface than the one required of the provider, using the Adapter pattern [Gamma95, p. 139]. Here is a simple implementation with a service provider interface and a default provider: // Service provider framework sketch // Service interface public interface Service { ... // Service-specific methods go here } // Service provider interface public interface Provider { Service newService(); } // Noninstantiable class for service registration and access public class Services { private Services() { } // Prevents instantiation (Item 4) // Maps service names to services private static final Map providers = new ConcurrentHashMap(); public static final String DEFAULT_PROVIDER_NAME = ""; www.it-ebooks.info ITEM 1: CONSIDER STATIC FACTORY METHODS INSTEAD OF CONSTRUCTORS 9 // Provider registration API public static void registerDefaultProvider(Provider p) { registerProvider(DEFAULT_PROVIDER_NAME, p); } public static void registerProvider(String name, Provider p){ providers.put(name, p); } // Service access API public static Service newInstance() { return newInstance(DEFAULT_PROVIDER_NAME); } public static Service newInstance(String name) { Provider p = providers.get(name); if (p == null) throw new IllegalArgumentException( "No provider registered with name: " + name); return p.newService(); } } A fourth advantage of static factory methods is that they reduce the ver- bosity of creating parameterized type instances. Unfortunately, you must spec- ify the type parameters when you invoke the constructor of a parameterized class even if they’re obvious from context. This typically requires you to provide the type parameters twice in quick succession: Map> m = new HashMap>(); This redundant specification quickly becomes painful as the length and complex- ity of the type parameters increase. With static factories, however, the compiler can figure out the type parameters for you. This is known as type inference. For example, suppose that HashMap provided this static factory: public static HashMap newInstance() { return new HashMap(); } Then you could replace the wordy declaration above with this succinct alternative: Map> m = HashMap.newInstance(); Someday the language may perform this sort of type inference on constructor invocations as well as method invocations, but as of release 1.6, it does not. www.it-ebooks.info CHAPTER 2 CREATING AND DESTROYING OBJECTS10 Unfortunately, the standard collection implementations such as HashMap do not have factory methods as of release 1.6, but you can put these methods in your own utility class. More importantly, you can provide such static factories in your own parameterized classes. The main disadvantage of providing only static factory methods is that classes without public or protected constructors cannot be subclassed. The same is true for nonpublic classes returned by public static factories. For example, it is impossible to subclass any of the convenience implementation classes in the Collections Framework. Arguably this can be a blessing in disguise, as it encour- ages programmers to use composition instead of inheritance (Item 16). A second disadvantage of static factory methods is that they are not readily distinguishable from other static methods. They do not stand out in API documentation in the way that constructors do, so it can be difficult to figure out how to instantiate a class that provides static factory methods instead of construc- tors. The Javadoc tool may someday draw attention to static factory methods. In the meantime, you can reduce this disadvantage by drawing attention to static fac- tories in class or interface comments, and by adhering to common naming conven- tions. Here are some common names for static factory methods: • valueOf—Returns an instance that has, loosely speaking, the same value as its parameters. Such static factories are effectively type-conversion methods. • of—A concise alternative to valueOf, popularized by EnumSet (Item 32). • getInstance—Returns an instance that is described by the parameters but cannot be said to have the same value. In the case of a singleton, getInstance takes no parameters and returns the sole instance. • newInstance—Like getInstance, except that newInstance guarantees that each instance returned is distinct from all others. • getType—Like getInstance, but used when the factory method is in a differ- ent class. Type indicates the type of object returned by the factory method. • newType—Like newInstance, but used when the factory method is in a differ- ent class. Type indicates the type of object returned by the factory method. In summary, static factory methods and public constructors both have their uses, and it pays to understand their relative merits. Often static factories are pref- erable, so avoid the reflex to provide public constructors without first considering static factories. www.it-ebooks.info ITEM 2: CONSIDER A BUILDER WHEN FACED WITH MANY CONSTRUCTOR PARAMETERS 11 Item 2: Consider a builder when faced with many constructor parameters Static factories and constructors share a limitation: they do not scale well to large numbers of optional parameters. Consider the case of a class representing the Nutrition Facts label that appears on packaged foods. These labels have a few required fields—serving size, servings per container, and calories per serving— and over twenty optional fields—total fat, saturated fat, trans fat, cholesterol, sodium, and so on. Most products have nonzero values for only a few of these optional fields. What sort of constructors or static factories should you write for such a class? Traditionally, programmers have used the telescoping constructor pattern, in which you provide a constructor with only the required parameters, another with a single optional parameter, a third with two optional parameters, and so on, culmi- nating in a constructor with all the optional parameters. Here’s how it looks in practice. For brevity’s sake, only four optional fields are shown: // Telescoping constructor pattern - does not scale well! public class NutritionFacts { private final int servingSize; // (mL) required private final int servings; // (per container) required private final int calories; // optional private final int fat; // (g) optional private final int sodium; // (mg) optional private final int carbohydrate; // (g) optional public NutritionFacts(int servingSize, int servings) { this(servingSize, servings, 0); } public NutritionFacts(int servingSize, int servings, int calories) { this(servingSize, servings, calories, 0); } public NutritionFacts(int servingSize, int servings, int calories, int fat) { this(servingSize, servings, calories, fat, 0); } public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium) { this(servingSize, servings, calories, fat, sodium, 0); } www.it-ebooks.info CHAPTER 2 CREATING AND DESTROYING OBJECTS12 public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium, int carbohydrate) { this.servingSize = servingSize; this.servings = servings; this.calories = calories; this.fat = fat; this.sodium = sodium; this.carbohydrate = carbohydrate; } } When you want to create an instance, you use the constructor with the shortest parameter list containing all the parameters you want to set: NutritionFacts cocaCola = new NutritionFacts(240, 8, 100, 0, 35, 27); Typically this constructor invocation will require many parameters that you don’t want to set, but you’re forced to pass a value for them anyway. In this case, we passed a value of 0 for fat. With “only” six parameters this may not seem so bad, but it quickly gets out of hand as the number of parameters increases. In short, the telescoping constructor pattern works, but it is hard to write client code when there are many parameters, and harder still to read it. The reader is left wondering what all those values mean and must carefully count parameters to find out. Long sequences of identically typed parameters can cause subtle bugs. If the client accidentally reverses two such parameters, the compiler won’t complain, but the program will misbehave at runtime (Item 40). A second alternative when you are faced with many constructor parameters is the JavaBeans pattern, in which you call a parameterless constructor to create the object and then call setter methods to set each required parameter and each optional parameter of interest: // JavaBeans Pattern - allows inconsistency, mandates mutability public class NutritionFacts { // Parameters initialized to default values (if any) private int servingSize = -1; // Required; no default value private int servings = -1; // " " " " private int calories = 0; private int fat = 0; private int sodium = 0; private int carbohydrate = 0; public NutritionFacts() { } www.it-ebooks.info ITEM 2: CONSIDER A BUILDER WHEN FACED WITH MANY CONSTRUCTOR PARAMETERS 13 // Setters public void setServingSize(int val) { servingSize = val; } public void setServings(int val) { servings = val; } public void setCalories(int val) { calories = val; } public void setFat(int val) { fat = val; } public void setSodium(int val) { sodium = val; } public void setCarbohydrate(int val) { carbohydrate = val; } } This pattern has none of the disadvantages of the telescoping constructor pattern. It is easy, if a bit wordy, to create instances, and easy to read the resulting code: NutritionFacts cocaCola = new NutritionFacts(); cocaCola.setServingSize(240); cocaCola.setServings(8); cocaCola.setCalories(100); cocaCola.setSodium(35); cocaCola.setCarbohydrate(27); Unfortunately, the JavaBeans pattern has serious disadvantages of its own. Because construction is split across multiple calls, a JavaBean may be in an inconsistent state partway through its construction. The class does not have the option of enforcing consistency merely by checking the validity of the con- structor parameters. Attempting to use an object when it’s in an inconsistent state may cause failures that are far removed from the code containing the bug, hence difficult to debug. A related disadvantage is that the JavaBeans pattern pre- cludes the possibility of making a class immutable (Item 15), and requires added effort on the part of the programmer to ensure thread safety. It is possible to reduce these disadvantages by manually “freezing” the object when its construction is complete and not allowing it to be used until frozen, but this variant is unwieldy and rarely used in practice. Moreover, it can cause errors at runtime, as the compiler cannot ensure that the programmer calls the freeze method on an object before using it. Luckily, there is a third alternative that combines the safety of the telescoping constructor pattern with the readability of the JavaBeans pattern. It is a form of the Builder pattern [Gamma95, p. 97]. Instead of making the desired object directly, the client calls a constructor (or static factory) with all of the required parameters and gets a builder object. Then the client calls setter-like methods on the builder object to set each optional parameter of interest. Finally, the client calls a parame- terless build method to generate the object, which is immutable. The builder is a static member class (Item 22) of the class it builds. Here’s how it looks in practice: www.it-ebooks.info CHAPTER 2 CREATING AND DESTROYING OBJECTS14 // Builder Pattern public class NutritionFacts { private final int servingSize; private final int servings; private final int calories; private final int fat; private final int sodium; private final int carbohydrate; public static class Builder { // Required parameters private final int servingSize; private final int servings; // Optional parameters - initialized to default values private int calories = 0; private int fat = 0; private int carbohydrate = 0; private int sodium = 0; public Builder(int servingSize, int servings) { this.servingSize = servingSize; this.servings = servings; } public Builder calories(int val) { calories = val; return this; } public Builder fat(int val) { fat = val; return this; } public Builder carbohydrate(int val) { carbohydrate = val; return this; } public Builder sodium(int val) { sodium = val; return this; } public NutritionFacts build() { return new NutritionFacts(this); } } private NutritionFacts(Builder builder) { servingSize = builder.servingSize; servings = builder.servings; calories = builder.calories; fat = builder.fat; sodium = builder.sodium; carbohydrate = builder.carbohydrate; } } www.it-ebooks.info ITEM 2: CONSIDER A BUILDER WHEN FACED WITH MANY CONSTRUCTOR PARAMETERS 15 Note that NutritionFacts is immutable, and that all parameter default values are in a single location. The builder’s setter methods return the builder itself so that invocations can be chained. Here’s how the client code looks: NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8). calories(100).sodium(35).carbohydrate(27).build(); This client code is easy to write and, more importantly, to read. The Builder pat- tern simulates named optional parameters as found in Ada and Python. Like a constructor, a builder can impose invariants on its parameters. The build method can check these invariants. It is critical that they be checked after copying the parameters from the builder to the object, and that they be checked on the object fields rather than the builder fields (Item 39). If any invariants are vio- lated, the build method should throw an IllegalStateException (Item 60). The exception’s detail method should indicate which invariant is violated (Item 63). Another way to impose invariants involving multiple parameters is to have setter methods take entire groups of parameters on which some invariant must hold. If the invariant isn’t satisfied, the setter method throws an IllegalArgu- mentException. This has the advantage of detecting the invariant failure as soon as the invalid parameters are passed, instead of waiting for build to be invoked. A minor advantage of builders over constructors is that builders can have mul- tiple varargs parameters. Constructors, like methods, can have only one varargs parameter. Because builders use separate methods to set each parameter, they can have as many varargs parameters as you like, up to one per setter method. The Builder pattern is flexible. A single builder can be used to build multiple objects. The parameters of the builder can be tweaked between object creations to vary the objects. The builder can fill in some fields automatically, such as a serial number that automatically increases each time an object is created. A builder whose parameters have been set makes a fine Abstract Factory [Gamma95, p. 87]. In other words, a client can pass such a builder to a method to enable the method to create one or more objects for the client. To enable this usage, you need a type to represent the builder. If you are using release 1.5 or a later release, a single generic type (Item 26) suffices for all builders, no matter what type of object they’re building: // A builder for objects of type T public interface Builder { public T build(); } www.it-ebooks.info CHAPTER 2 CREATING AND DESTROYING OBJECTS16 Note that our NutritionFacts.Builder class could be declared to implement Builder. Methods that take a Builder instance would typically constrain the builder’s type parameter using a bounded wildcard type (Item 28). For example, here is a method that builds a tree using a client-provided Builder instance to build each node: Tree buildTree(Builder nodeBuilder) { ... } The traditional Abstract Factory implementation in Java has been the Class object, with the newInstance method playing the part of the build method. This usage is fraught with problems. The newInstance method always attempts to invoke the class’s parameterless constructor, which may not even exist. You don’t get a compile-time error if the class has no accessible parameterless constructor. Instead, the client code must cope with InstantiationException or IllegalAc- cessException at runtime, which is ugly and inconvenient. Also, the newIn- stance method propagates any exceptions thrown by the parameterless constructor, even though newInstance lacks the corresponding throws clauses. In other words, Class.newInstance breaks compile-time exception checking. The Builder interface, shown above, corrects these deficiencies. The Builder pattern does have disadvantages of its own. In order to create an object, you must first create its builder. While the cost of creating the builder is unlikely to be noticeable in practice, it could be a problem in some performance- critical situations. Also, the Builder pattern is more verbose than the telescoping constructor pattern, so it should be used only if there are enough parameters, say, four or more. But keep in mind that you may want to add parameters in the future. If you start out with constructors or static factories, and add a builder when the class evolves to the point where the number of parameters starts to get out of hand, the obsolete constructors or static factories will stick out like a sore thumb. There- fore, it’s often better to start with a builder in the first place. In summary, the Builder pattern is a good choice when designing classes whose constructors or static factories would have more than a handful of parameters, especially if most of those parameters are optional. Client code is much easier to read and write with builders than with the traditional telescoping constructor pattern, and builders are much safer than JavaBeans. www.it-ebooks.info ITEM 3: ENFORCE THE SINGLETON PROPERTY WITH A PRIVATE CONSTRUCTOR OR AN ENUM TYPE 17 Item 3: Enforce the singleton property with a private constructor or an enum type A singleton is simply a class that is instantiated exactly once [Gamma95, p. 127]. Singletons typically represent a system component that is intrinsically unique, such as the window manager or file system. Making a class a singleton can make it difficult to test its clients, as it’s impossible to substitute a mock imple- mentation for a singleton unless it implements an interface that serves as its type. Before release 1.5, there were two ways to implement singletons. Both are based on keeping the constructor private and exporting a public static member to provide access to the sole instance. In one approach, the member is a final field: // Singleton with public final field public class Elvis { public static final Elvis INSTANCE = new Elvis(); private Elvis() { ... } public void leaveTheBuilding() { ... } } The private constructor is called only once, to initialize the public static final field Elvis.INSTANCE. The lack of a public or protected constructor guarantees a “monoelvistic” universe: exactly one Elvis instance will exist once the Elvis class is initialized—no more, no less. Nothing that a client does can change this, with one caveat: a privileged client can invoke the private constructor reflectively (Item 53) with the aid of the AccessibleObject.setAccessible method. If you need to defend against this attack, modify the constructor to make it throw an exception if it’s asked to create a second instance. In the second approach to implementing singletons, the public member is a static factory method: // Singleton with static factory public class Elvis { private static final Elvis INSTANCE = new Elvis(); private Elvis() { ... } public static Elvis getInstance() { return INSTANCE; } public void leaveTheBuilding() { ... } } All calls to Elvis.getInstance return the same object reference, and no other Elvis instance will ever be created (with the same caveat mentioned above). www.it-ebooks.info CHAPTER 2 CREATING AND DESTROYING OBJECTS18 The main advantage of the public field approach is that the declarations make it clear that the class is a singleton: the public static field is final, so it will always contain the same object reference. There is no longer any performance advantage to the public field approach: modern Java virtual machine (JVM) implementations are almost certain to inline the call to the static factory method. One advantage of the factory-method approach is that it gives you the flexibil- ity to change your mind about whether the class should be a singleton without changing its API. The factory method returns the sole instance but could easily be modified to return, say, a unique instance for each thread that invokes it. A second advantage, concerning generic types, is discussed in Item 27. Often neither of these advantages is relevant, and the final-field approach is simpler. To make a singleton class that is implemented using either of the previous approaches serializable (Chapter 11), it is not sufficient merely to add imple- ments Serializable to its declaration. To maintain the singleton guarantee, you have to declare all instance fields transient and provide a readResolve method (Item 77). Otherwise, each time a serialized instance is deserialized, a new instance will be created, leading, in the case of our example, to spurious Elvis sightings. To prevent this, add this readResolve method to the Elvis class: // readResolve method to preserve singleton property private Object readResolve() { // Return the one true Elvis and let the garbage collector // take care of the Elvis impersonator. return INSTANCE; } As of release 1.5, there is a third approach to implementing singletons. Simply make an enum type with one element: // Enum singleton - the preferred approach public enum Elvis { INSTANCE; public void leaveTheBuilding() { ... } } This approach is functionally equivalent to the public field approach, except that it is more concise, provides the serialization machinery for free, and provides an ironclad guarantee against multiple instantiation, even in the face of sophisticated serialization or reflection attacks. While this approach has yet to be widely adopted, a single-element enum type is the best way to implement a singleton. www.it-ebooks.info ITEM 4: ENFORCE NONINSTANTIABILITY WITH A PRIVATE CONSTRUCTOR 19 Item 4: Enforce noninstantiability with a private constructor Occasionally you’ll want to write a class that is just a grouping of static methods and static fields. Such classes have acquired a bad reputation because some people abuse them to avoid thinking in terms of objects, but they do have valid uses. They can be used to group related methods on primitive values or arrays, in the manner of java.lang.Math or java.util.Arrays. They can also be used to group static methods, including factory methods (Item 1), for objects that implement a particu- lar interface, in the manner of java.util.Collections. Lastly, they can be used to group methods on a final class, instead of extending the class. Such utility classes were not designed to be instantiated: an instance would be nonsensical. In the absence of explicit constructors, however, the compiler pro- vides a public, parameterless default constructor. To a user, this constructor is indistinguishable from any other. It is not uncommon to see unintentionally instantiable classes in published APIs. Attempting to enforce noninstantiability by making a class abstract does not work. The class can be subclassed and the subclass instantiated. Furthermore, it misleads the user into thinking the class was designed for inheritance (Item 17). There is, however, a simple idiom to ensure noninstantiability. A default construc- tor is generated only if a class contains no explicit constructors, so a class can be made noninstantiable by including a private constructor: // Noninstantiable utility class public class UtilityClass { // Suppress default constructor for noninstantiability private UtilityClass() { throw new AssertionError(); } ... // Remainder omitted } Because the explicit constructor is private, it is inaccessible outside of the class. The AssertionError isn’t strictly required, but it provides insurance in case the constructor is accidentally invoked from within the class. It guarantees that the class will never be instantiated under any circumstances. This idiom is mildly counterintuitive, as the constructor is provided expressly so that it cannot be invoked. It is therefore wise to include a comment, as shown above. As a side effect, this idiom also prevents the class from being subclassed. All constructors must invoke a superclass constructor, explicitly or implicitly, and a subclass would have no accessible superclass constructor to invoke. www.it-ebooks.info CHAPTER 2 CREATING AND DESTROYING OBJECTS20 Item 5: Avoid creating unnecessary objects It is often appropriate to reuse a single object instead of creating a new function- ally equivalent object each time it is needed. Reuse can be both faster and more stylish. An object can always be reused if it is immutable (Item 15). As an extreme example of what not to do, consider this statement: String s = new String("stringette"); // DON'T DO THIS! The statement creates a new String instance each time it is executed, and none of those object creations is necessary. The argument to the String construc- tor ("stringette") is itself a String instance, functionally identical to all of the objects created by the constructor. If this usage occurs in a loop or in a frequently invoked method, millions of String instances can be created needlessly. The improved version is simply the following: String s = "stringette"; This version uses a single String instance, rather than creating a new one each time it is executed. Furthermore, it is guaranteed that the object will be reused by any other code running in the same virtual machine that happens to con- tain the same string literal [JLS, 3.10.5]. You can often avoid creating unnecessary objects by using static factory meth- ods (Item 1) in preference to constructors on immutable classes that provide both. For example, the static factory method Boolean.valueOf(String) is almost always preferable to the constructor Boolean(String). The constructor creates a new object each time it’s called, while the static factory method is never required to do so and won’t in practice. In addition to reusing immutable objects, you can also reuse mutable objects if you know they won’t be modified. Here is a slightly more subtle, and much more common, example of what not to do. It involves mutable Date objects that are never modified once their values have been computed. This class models a person and has an isBabyBoomer method that tells whether the person is a “baby boomer,” in other words, whether the person was born between 1946 and 1964: public class Person { private final Date birthDate; // Other fields, methods, and constructor omitted www.it-ebooks.info ITEM 5: AVOID CREATING UNNECESSARY OBJECTS 21 // DON'T DO THIS! public boolean isBabyBoomer() { // Unnecessary allocation of expensive object Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0); Date boomStart = gmtCal.getTime(); gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0); Date boomEnd = gmtCal.getTime(); return birthDate.compareTo(boomStart) >= 0 && birthDate.compareTo(boomEnd) < 0; } } The isBabyBoomer method unnecessarily creates a new Calendar, TimeZone, and two Date instances each time it is invoked. The version that follows avoids this inefficiency with a static initializer: class Person { private final Date birthDate; // Other fields, methods, and constructor omitted /** * The starting and ending dates of the baby boom. */ private static final Date BOOM_START; private static final Date BOOM_END; static { Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0); BOOM_START = gmtCal.getTime(); gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0); BOOM_END = gmtCal.getTime(); } public boolean isBabyBoomer() { return birthDate.compareTo(BOOM_START) >= 0 && birthDate.compareTo(BOOM_END) < 0; } } The improved version of the Person class creates Calendar, TimeZone, and Date instances only once, when it is initialized, instead of creating them every time isBabyBoomer is invoked. This results in significant performance gains if the www.it-ebooks.info CHAPTER 2 CREATING AND DESTROYING OBJECTS22 method is invoked frequently. On my machine, the original version takes 32,000 ms for 10 million invocations, while the improved version takes 130 ms, which is about 250 times faster. Not only is performance improved, but so is clarity. Changing boomStart and boomEnd from local variables to static final fields makes it clear that these dates are treated as constants, making the code more understand- able. In the interest of full disclosure, the savings from this sort of optimization will not always be this dramatic, as Calendar instances are particularly expensive to create. If the improved version of the Person class is initialized but its isBabyBoomer method is never invoked, the BOOM_START and BOOM_END fields will be initialized unnecessarily. It would be possible to eliminate the unnecessary initializations by lazily initializing these fields (Item 71) the first time the isBabyBoomer method is invoked, but it is not recommended. As is often the case with lazy initialization, it would complicate the implementation and would be unlikely to result in a notice- able performance improvement beyond what we’ve already achieved (Item 55). In the previous examples in this item, it was obvious that the objects in ques- tion could be reused because they were not modified after initialization. There are other situations where it is less obvious. Consider the case of adapters [Gamma95, p. 139], also known as views. An adapter is an object that delegates to a backing object, providing an alternative interface to the backing object. Because an adapter has no state beyond that of its backing object, there’s no need to create more than one instance of a given adapter to a given object. For example, the keySet method of the Map interface returns a Set view of the Map object, consisting of all the keys in the map. Naively, it would seem that every call to keySet would have to create a new Set instance, but every call to keySet on a given Map object may return the same Set instance. Although the returned Set instance is typically mutable, all of the returned objects are functionally iden- tical: when one of the returned objects changes, so do all the others because they’re all backed by the same Map instance. While it is harmless to create multiple instances of the keySet view object, it is also unnecessary. There’s a new way to create unnecessary objects in release 1.5. It is called autoboxing, and it allows the programmer to mix primitive and boxed primitive types, boxing and unboxing automatically as needed. Autoboxing blurs but does not erase the distinction between primitive and boxed primitive types. There are subtle semantic distinctions, and not-so-subtle performance differences (Item 49). Consider the following program, which calculates the sum of all the positive int www.it-ebooks.info ITEM 5: AVOID CREATING UNNECESSARY OBJECTS 23 values. To do this, the program has to use long arithmetic, because an int is not big enough to hold the sum of all the positive int values: // Hideously slow program! Can you spot the object creation? public static void main(String[] args) { Long sum = 0L; for (long i = 0; i < Integer.MAX_VALUE; i++) { sum += i; } System.out.println(sum); } This program gets the right answer, but it is much slower than it should be, due to a one-character typographical error. The variable sum is declared as a Long instead of a long, which means that the program constructs about 231 unnecessary Long instances (roughly one for each time the long i is added to the Long sum). Changing the declaration of sum from Long to long reduces the runtime from 43 seconds to 6.8 seconds on my machine. The lesson is clear: prefer primitives to boxed primitives, and watch out for unintentional autoboxing. This item should not be misconstrued to imply that object creation is expen- sive and should be avoided. On the contrary, the creation and reclamation of small objects whose constructors do little explicit work is cheap, especially on modern JVM implementations. Creating additional objects to enhance the clarity, simplic- ity, or power of a program is generally a good thing. Conversely, avoiding object creation by maintaining your own object pool is a bad idea unless the objects in the pool are extremely heavyweight. The classic example of an object that does justify an object pool is a database connection. The cost of establishing the connection is sufficiently high that it makes sense to reuse these objects. Also, your database license may limit you to a fixed number of con- nections. Generally speaking, however, maintaining your own object pools clut- ters your code, increases memory footprint, and harms performance. Modern JVM implementations have highly optimized garbage collectors that easily out- perform such object pools on lightweight objects. The counterpoint to this item is Item 39 on defensive copying. Item 5 says, “Don’t create a new object when you should reuse an existing one,” while Item 39 says, “Don’t reuse an existing object when you should create a new one.” Note that the penalty for reusing an object when defensive copying is called for is far greater than the penalty for needlessly creating a duplicate object. Failing to make defensive copies where required can lead to insidious bugs and security holes; cre- ating objects unnecessarily merely affects style and performance. www.it-ebooks.info CHAPTER 2 CREATING AND DESTROYING OBJECTS24 Item 6: Eliminate obsolete object references When you switch from a language with manual memory management, such as C or C++, to a garbage-collected language, your job as a programmer is made much easier by the fact that your objects are automatically reclaimed when you’re through with them. It seems almost like magic when you first experience it. It can easily lead to the impression that you don’t have to think about memory manage- ment, but this isn’t quite true. Consider the following simple stack implementation: // Can you spot the "memory leak"? public class Stack { private Object[] elements; private int size = 0; private static final int DEFAULT_INITIAL_CAPACITY = 16; public Stack() { elements = new Object[DEFAULT_INITIAL_CAPACITY]; } public void push(Object e) { ensureCapacity(); elements[size++] = e; } public Object pop() { if (size == 0) throw new EmptyStackException(); return elements[--size]; } /** * Ensure space for at least one more element, roughly * doubling the capacity each time the array needs to grow. */ private void ensureCapacity() { if (elements.length == size) elements = Arrays.copyOf(elements, 2 * size + 1); } } There’s nothing obviously wrong with this program (but see Item 26 for a generic version). You could test it exhaustively, and it would pass every test with flying colors, but there’s a problem lurking. Loosely speaking, the program has a “memory leak,” which can silently manifest itself as reduced performance due to www.it-ebooks.info ITEM 6: ELIMINATE OBSOLETE OBJECT REFERENCES 25 increased garbage collector activity or increased memory footprint. In extreme cases, such memory leaks can cause disk paging and even program failure with an OutOfMemoryError, but such failures are relatively rare. So where is the memory leak? If a stack grows and then shrinks, the objects that were popped off the stack will not be garbage collected, even if the program using the stack has no more references to them. This is because the stack main- tains obsolete references to these objects. An obsolete reference is simply a refer- ence that will never be dereferenced again. In this case, any references outside of the “active portion” of the element array are obsolete. The active portion consists of the elements whose index is less than size. Memory leaks in garbage-collected languages (more properly known as unin- tentional object retentions) are insidious. If an object reference is unintentionally retained, not only is that object excluded from garbage collection, but so too are any objects referenced by that object, and so on. Even if only a few object refer- ences are unintentionally retained, many, many objects may be prevented from being garbage collected, with potentially large effects on performance. The fix for this sort of problem is simple: null out references once they become obsolete. In the case of our Stack class, the reference to an item becomes obsolete as soon as it’s popped off the stack. The corrected version of the pop method looks like this: public Object pop() { if (size == 0) throw new EmptyStackException(); Object result = elements[--size]; elements[size] = null; // Eliminate obsolete reference return result; } An added benefit of nulling out obsolete references is that, if they are subse- quently dereferenced by mistake, the program will immediately fail with a NullPointerException, rather than quietly doing the wrong thing. It is always beneficial to detect programming errors as quickly as possible. When programmers are first stung by this problem, they may overcompensate by nulling out every object reference as soon as the program is finished using it. This is neither necessary nor desirable, as it clutters up the program unnecessarily. Nulling out object references should be the exception rather than the norm. The best way to eliminate an obsolete reference is to let the variable that contained the reference fall out of scope. This occurs naturally if you define each variable in the narrowest possible scope (Item 45). www.it-ebooks.info CHAPTER 2 CREATING AND DESTROYING OBJECTS26 So when should you null out a reference? What aspect of the Stack class makes it susceptible to memory leaks? Simply put, it manages its own memory. The storage pool consists of the elements of the elements array (the object refer- ence cells, not the objects themselves). The elements in the active portion of the array (as defined earlier) are allocated, and those in the remainder of the array are free. The garbage collector has no way of knowing this; to the garbage collector, all of the object references in the elements array are equally valid. Only the pro- grammer knows that the inactive portion of the array is unimportant. The pro- grammer effectively communicates this fact to the garbage collector by manually nulling out array elements as soon as they become part of the inactive portion. Generally speaking, whenever a class manages its own memory, the pro- grammer should be alert for memory leaks. Whenever an element is freed, any object references contained in the element should be nulled out. Another common source of memory leaks is caches. Once you put an object reference into a cache, it’s easy to forget that it’s there and leave it in the cache long after it becomes irrelevant. There are several solutions to this problem. If you’re lucky enough to implement a cache for which an entry is relevant exactly so long as there are references to its key outside of the cache, represent the cache as a WeakHashMap; entries will be removed automatically after they become obso- lete. Remember that WeakHashMap is useful only if the desired lifetime of cache entries is determined by external references to the key, not the value. More commonly, the useful lifetime of a cache entry is less well defined, with entries becoming less valuable over time. Under these circumstances, the cache should occasionally be cleansed of entries that have fallen into disuse. This can be done by a background thread (perhaps a Timer or ScheduledThreadPoolExecu- tor) or as a side effect of adding new entries to the cache. The LinkedHashMap class facilitates the latter approach with its removeEldestEntry method. For more sophisticated caches, you may need to use java.lang.ref directly. A third common source of memory leaks is listeners and other callbacks. If you implement an API where clients register callbacks but don’t deregister them explicitly, they will accumulate unless you take some action. The best way to ensure that callbacks are garbage collected promptly is to store only weak refer- ences to them, for instance, by storing them only as keys in a WeakHashMap. Because memory leaks typically do not manifest themselves as obvious fail- ures, they may remain present in a system for years. They are typically discovered only as a result of careful code inspection or with the aid of a debugging tool known as a heap profiler. Therefore, it is very desirable to learn to anticipate prob- lems like this before they occur and prevent them from happening. www.it-ebooks.info ITEM 7: AVOID FINALIZERS 27 Item 7: Avoid finalizers Finalizers are unpredictable, often dangerous, and generally unnecessary. Their use can cause erratic behavior, poor performance, and portability problems. Finalizers have a few valid uses, which we’ll cover later in this item, but as a rule of thumb, you should avoid finalizers. C++ programmers are cautioned not to think of finalizers as Java’s analog of C++ destructors. In C++, destructors are the normal way to reclaim the resources associated with an object, a necessary counterpart to constructors. In Java, the gar- bage collector reclaims the storage associated with an object when it becomes unreachable, requiring no special effort on the part of the programmer. C++ destructors are also used to reclaim other nonmemory resources. In Java, the try- finally block is generally used for this purpose. One shortcoming of finalizers is that there is no guarantee they’ll be executed promptly [JLS, 12.6]. It can take arbitrarily long between the time that an object becomes unreachable and the time that its finalizer is executed. This means that you should never do anything time-critical in a finalizer. For example, it is a grave error to depend on a finalizer to close files, because open file descriptors are a limited resource. If many files are left open because the JVM is tardy in execut- ing finalizers, a program may fail because it can no longer open files. The promptness with which finalizers are executed is primarily a function of the garbage collection algorithm, which varies widely from JVM implementation to JVM implementation. The behavior of a program that depends on the prompt- ness of finalizer execution may likewise vary. It is entirely possible that such a program will run perfectly on the JVM on which you test it and then fail miserably on the JVM favored by your most important customer. Tardy finalization is not just a theoretical problem. Providing a finalizer for a class can, under rare conditions, arbitrarily delay reclamation of its instances. A colleague debugged a long-running GUI application that was mysteriously dying with an OutOfMemoryError. Analysis revealed that at the time of its death, the application had thousands of graphics objects on its finalizer queue just waiting to be finalized and reclaimed. Unfortunately, the finalizer thread was running at a lower priority than another application thread, so objects weren’t getting finalized at the rate they became eligible for finalization. The language specification makes no guarantees as to which thread will execute finalizers, so there is no portable way to prevent this sort of problem other than to refrain from using finalizers. Not only does the language specification provide no guarantee that finalizers will get executed promptly; it provides no guarantee that they’ll get executed at www.it-ebooks.info CHAPTER 2 CREATING AND DESTROYING OBJECTS28 all. It is entirely possible, even likely, that a program terminates without executing finalizers on some objects that are no longer reachable. As a consequence, you should never depend on a finalizer to update critical persistent state. For example, depending on a finalizer to release a persistent lock on a shared resource such as a database is a good way to bring your entire distributed system to a grinding halt. Don’t be seduced by the methods System.gc and System.runFinalization. They may increase the odds of finalizers getting executed, but they don’t guaran- tee it. The only methods that claim to guarantee finalization are System.runFi- nalizersOnExit and its evil twin, Runtime.runFinalizersOnExit. These methods are fatally flawed and have been deprecated [ThreadStop]. In case you are not yet convinced that finalizers should be avoided, here’s another tidbit worth considering: if an uncaught exception is thrown during final- ization, the exception is ignored, and finalization of that object terminates [JLS, 12.6]. Uncaught exceptions can leave objects in a corrupt state. If another thread attempts to use such a corrupted object, arbitrary nondeterministic behavior may result. Normally, an uncaught exception will terminate the thread and print a stack trace, but not if it occurs in a finalizer—it won’t even print a warning. Oh, and one more thing: there is a severe performance penalty for using finalizers. On my machine, the time to create and destroy a simple object is about 5.6 ns. Adding a finalizer increases the time to 2,400 ns. In other words, it is about 430 times slower to create and destroy objects with finalizers. So what should you do instead of writing a finalizer for a class whose objects encapsulate resources that require termination, such as files or threads? Just pro- vide an explicit termination method, and require clients of the class to invoke this method on each instance when it is no longer needed. One detail worth mention- ing is that the instance must keep track of whether it has been terminated: the explicit termination method must record in a private field that the object is no longer valid, and other methods must check this field and throw an Illegal- StateException if they are called after the object has been terminated. Typical examples of explicit termination methods are the close methods on InputStream, OutputStream, and java.sql.Connection. Another example is the cancel method on java.util.Timer, which performs the necessary state change to cause the thread associated with a Timer instance to terminate itself gently. Examples from java.awt include Graphics.dispose and Window.dis- pose. These methods are often overlooked, with predictably dire performance consequences. A related method is Image.flush, which deallocates all the www.it-ebooks.info ITEM 7: AVOID FINALIZERS 29 resources associated with an Image instance but leaves it in a state where it can still be used, reallocating the resources if necessary. Explicit termination methods are typically used in combination with the try-finally construct to ensure termination. Invoking the explicit termination method inside the finally clause ensures that it will get executed even if an exception is thrown while the object is being used: // try-finally block guarantees execution of termination method Foo foo = new Foo(...); try { // Do what must be done with foo ... } finally { foo.terminate(); // Explicit termination method } So what, if anything, are finalizers good for? There are perhaps two legitimate uses. One is to act as a “safety net” in case the owner of an object forgets to call its explicit termination method. While there’s no guarantee that the finalizer will be invoked promptly, it may be better to free the resource late than never, in those (hopefully rare) cases when the client fails to call the explicit termination method. But the finalizer should log a warning if it finds that the resource has not been terminated, as this indicates a bug in the client code, which should be fixed. If you are considering writing such a safety-net finalizer, think long and hard about whether the extra protection is worth the extra cost. The four classes cited as examples of the explicit termination method pattern (FileInputStream, FileOutputStream, Timer, and Connection) have finalizers that serve as safety nets in case their termination methods aren’t called. Unfortu- nately these finalizers do not log warnings. Such warnings generally can’t be added after an API is published, as it would appear to break existing clients. A second legitimate use of finalizers concerns objects with native peers. A native peer is a native object to which a normal object delegates via native meth- ods. Because a native peer is not a normal object, the garbage collector doesn’t know about it and can’t reclaim it when its Java peer is reclaimed. A finalizer is an appropriate vehicle for performing this task, assuming the native peer holds no critical resources. If the native peer holds resources that must be terminated promptly, the class should have an explicit termination method, as described above. The termination method should do whatever is required to free the critical resource. The termination method can be a native method, or it can invoke one. www.it-ebooks.info CHAPTER 2 CREATING AND DESTROYING OBJECTS30 It is important to note that “finalizer chaining” is not performed automatically. If a class (other than Object) has a finalizer and a subclass overrides it, the sub- class finalizer must invoke the superclass finalizer manually. You should finalize the subclass in a try block and invoke the superclass finalizer in the correspond- ing finally block. This ensures that the superclass finalizer gets executed even if the subclass finalization throws an exception and vice versa. Here’s how it looks. Note that this example uses the Override annotation (@Override), which was added to the platform in release 1.5. You can ignore Override annotations for now, or see Item 36 to find out what they mean: // Manual finalizer chaining @Override protected void finalize() throws Throwable { try { ... // Finalize subclass state } finally { super.finalize(); } } If a subclass implementor overrides a superclass finalizer but forgets to invoke it, the superclass finalizer will never be invoked. It is possible to defend against such a careless or malicious subclass at the cost of creating an additional object for every object to be finalized. Instead of putting the finalizer on the class requiring finalization, put the finalizer on an anonymous class (Item 22) whose sole purpose is to finalize its enclosing instance. A single instance of the anonymous class, called a finalizer guardian, is created for each instance of the enclosing class. The enclosing instance stores the sole reference to its finalizer guardian in a private instance field so the finalizer guardian becomes eligible for finalization at the same time as the enclosing instance. When the guardian is finalized, it performs the finalization activity desired for the enclosing instance, just as if its finalizer were a method on the enclosing class: // Finalizer Guardian idiom public class Foo { // Sole purpose of this object is to finalize outer Foo object private final Object finalizerGuardian = new Object() { @Override protected void finalize() throws Throwable { ... // Finalize outer Foo object } }; ... // Remainder omitted } www.it-ebooks.info ITEM 7: AVOID FINALIZERS 31 Note that the public class, Foo, has no finalizer (other than the trivial one it inherits from Object), so it doesn’t matter whether a subclass finalizer calls super.finalize or not. This technique should be considered for every nonfinal public class that has a finalizer. In summary, don’t use finalizers except as a safety net or to terminate noncritical native resources. In those rare instances where you do use a finalizer, remember to invoke super.finalize. If you use a finalizer as a safety net, remember to log the invalid usage from the finalizer. Lastly, if you need to associate a finalizer with a public, nonfinal class, consider using a finalizer guardian, so finalization can take place even if a subclass finalizer fails to invoke super.finalize. www.it-ebooks.info This page intentionally left blank www.it-ebooks.info 33 CHAPTER 3 Methods Common to All Objects ALTHOUGH Object is a concrete class, it is designed primarily for extension. All of its nonfinal methods (equals, hashCode, toString, clone, and finalize) have explicit general contracts because they are designed to be overridden. It is the responsibility of any class overriding these methods to obey their general con- tracts; failure to do so will prevent other classes that depend on the contracts (such as HashMap and HashSet) from functioning properly in conjunction with the class. This chapter tells you when and how to override the nonfinal Object methods. The finalize method is omitted from this chapter because it was discussed in Item 7. While not an Object method, Comparable.compareTo is discussed in this chapter because it has a similar character. Item 8: Obey the general contract when overriding equals Overriding the equals method seems simple, but there are many ways to get it wrong, and consequences can be dire. The easiest way to avoid problems is not to override the equals method, in which case each instance of the class is equal only to itself. This is the right thing to do if any of the following conditions apply: • Each instance of the class is inherently unique. This is true for classes such as Thread that represent active entities rather than values. The equals imple- mentation provided by Object has exactly the right behavior for these classes. • You don’t care whether the class provides a “logical equality” test. For example, java.util.Random could have overridden equals to check whether two Random instances would produce the same sequence of random numbers going forward, but the designers didn’t think that clients would need or want this functionality. Under these circumstances, the equals implementation inherited from Object is adequate. www.it-ebooks.info CHAPTER 3 METHODS COMMON TO ALL OBJECTS34 • A superclass has already overridden equals, and the superclass behavior is appropriate for this class. For example, most Set implementations inherit their equals implementation from AbstractSet, List implementations from AbstractList, and Map implementations from AbstractMap. • The class is private or package-private, and you are certain that its equals method will never be invoked. Arguably, the equals method should be over- ridden under these circumstances, in case it is accidentally invoked: @Override public boolean equals(Object o) { throw new AssertionError(); // Method is never called } So when is it appropriate to override Object.equals? When a class has a notion of logical equality that differs from mere object identity, and a superclass has not already overridden equals to implement the desired behavior. This is gen- erally the case for value classes. A value class is simply a class that represents a value, such as Integer or Date. A programmer who compares references to value objects using the equals method expects to find out whether they are logically equivalent, not whether they refer to the same object. Not only is overriding the equals method necessary to satisfy programmer expectations; it enables instances to serve as map keys or set elements with predictable, desirable behavior. One kind of value class that does not require the equals method to be overrid- den is a class that uses instance control (Item 1) to ensure that at most one object exists with each value. Enum types (Item 30) fall into this category. For these classes, logical equality is the same as object identity, so Object’s equals method functions as a logical equals method. When you override the equals method, you must adhere to its general con- tract. Here is the contract, copied from the specification for Object [JavaSE6]: The equals method implements an equivalence relation. It is: • Reflexive: For any non-null reference value x, x.equals(x) must return true. • Symmetric: For any non-null reference values x and y, x.equals(y) must re- turn true if and only if y.equals(x) returns true. • Transitive: For any non-null reference values x, y, z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) must return true. • Consistent: For any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, pro- vided no information used in equals comparisons on the objects is modified. • For any non-null reference value x, x.equals(null) must return false. www.it-ebooks.info ITEM 8: OBEY THE GENERAL CONTRACT WHEN OVERRIDING EQUALS 35 Unless you are mathematically inclined, this might look a bit scary, but do not ignore it! If you violate it, you may well find that your program behaves errati- cally or crashes, and it can be very difficult to pin down the source of the failure. To paraphrase John Donne, no class is an island. Instances of one class are fre- quently passed to another. Many classes, including all collections classes, depend on the objects passed to them obeying the equals contract. Now that you are aware of the dangers of violating the equals contract, let’s go over the contract in detail. The good news is that, appearances notwithstand- ing, the contract really isn’t very complicated. Once you understand it, it’s not hard to adhere to it. Let’s examine the five requirements in turn: Reflexivity—The first requirement says merely that an object must be equal to itself. It is hard to imagine violating this requirement unintentionally. If you were to violate it and then add an instance of your class to a collection, the collec- tion’s contains method might well say that the collection didn’t contain the instance that you just added. Symmetry—The second requirement says that any two objects must agree on whether they are equal. Unlike the first requirement, it’s not hard to imagine vio- lating this one unintentionally. For example, consider the following class, which implements a case-insensitive string. The case of the string is preserved by toString but ignored in comparisons: // Broken - violates symmetry! public final class CaseInsensitiveString { private final String s; public CaseInsensitiveString(String s) { if (s == null) throw new NullPointerException(); this.s = s; } // Broken - violates symmetry! @Override public boolean equals(Object o) { if (o instanceof CaseInsensitiveString) return s.equalsIgnoreCase( ((CaseInsensitiveString) o).s); if (o instanceof String) // One-way interoperability! return s.equalsIgnoreCase((String) o); return false; } ... // Remainder omitted } www.it-ebooks.info CHAPTER 3 METHODS COMMON TO ALL OBJECTS36 The well-intentioned equals method in this class naively attempts to interop- erate with ordinary strings. Let’s suppose that we have one case-insensitive string and one ordinary one: CaseInsensitiveString cis = new CaseInsensitiveString("Polish"); String s = "polish"; As expected, cis.equals(s) returns true. The problem is that while the equals method in CaseInsensitiveString knows about ordinary strings, the equals method in String is oblivious to case-insensitive strings. Therefore s.equals(cis) returns false, a clear violation of symmetry. Suppose you put a case-insensitive string into a collection: List list = new ArrayList(); list.add(cis); What does list.contains(s) return at this point? Who knows? In Sun’s cur- rent implementation, it happens to return false, but that’s just an implementation artifact. In another implementation, it could just as easily return true or throw a runtime exception. Once you’ve violated the equals contract, you simply don’t know how other objects will behave when confronted with your object. To eliminate the problem, merely remove the ill-conceived attempt to interop- erate with String from the equals method. Once you do this, you can refactor the method to give it a single return: @Override public boolean equals(Object o) { return o instanceof CaseInsensitiveString && ((CaseInsensitiveString) o).s.equalsIgnoreCase(s); } Transitivity—The third requirement of the equals contract says that if one object is equal to a second and the second object is equal to a third, then the first object must be equal to the third. Again, it’s not hard to imagine violating this requirement unintentionally. Consider the case of a subclass that adds a new value component to its superclass. In other words, the subclass adds a piece of informa- www.it-ebooks.info ITEM 8: OBEY THE GENERAL CONTRACT WHEN OVERRIDING EQUALS 37 tion that affects equals comparisons. Let’s start with a simple immutable two- dimensional integer point class: public class Point { private final int x; private final int y; public Point(int x, int y) { this.x = x; this.y = y; } @Override public boolean equals(Object o) { if (!(o instanceof Point)) return false; Point p = (Point)o; return p.x == x && p.y == y; } ... // Remainder omitted } Suppose you want to extend this class, adding the notion of color to a point: public class ColorPoint extends Point { private final Color color; public ColorPoint(int x, int y, Color color) { super(x, y); this.color = color; } ... // Remainder omitted } How should the equals method look? If you leave it out entirely, the imple- mentation is inherited from Point and color information is ignored in equals comparisons. While this does not violate the equals contract, it is clearly unac- ceptable. Suppose you write an equals method that returns true only if its argu- ment is another color point with the same position and color: // Broken - violates symmetry! @Override public boolean equals(Object o) { if (!(o instanceof ColorPoint)) return false; return super.equals(o) && ((ColorPoint) o).color == color; } www.it-ebooks.info CHAPTER 3 METHODS COMMON TO ALL OBJECTS38 The problem with this method is that you might get different results when comparing a point to a color point and vice versa. The former comparison ignores color, while the latter comparison always returns false because the type of the argument is incorrect. To make this concrete, let’s create one point and one color point: Point p = new Point(1, 2); ColorPoint cp = new ColorPoint(1, 2, Color.RED); Then p.equals(cp) returns true, while cp.equals(p) returns false. You might try to fix the problem by having ColorPoint.equals ignore color when doing “mixed comparisons”: // Broken - violates transitivity! @Override public boolean equals(Object o) { if (!(o instanceof Point)) return false; // If o is a normal Point, do a color-blind comparison if (!(o instanceof ColorPoint)) return o.equals(this); // o is a ColorPoint; do a full comparison return super.equals(o) && ((ColorPoint)o).color == color; } This approach does provide symmetry, but at the expense of transitivity: ColorPoint p1 = new ColorPoint(1, 2, Color.RED); Point p2 = new Point(1, 2); ColorPoint p3 = new ColorPoint(1, 2, Color.BLUE); Now p1.equals(p2) and p2.equals(p3) return true, while p1.equals(p3) returns false, a clear violation of transitivity. The first two comparisons are “color-blind,” while the third takes color into account. So what’s the solution? It turns out that this is a fundamental problem of equivalence relations in object-oriented languages. There is no way to extend an instantiable class and add a value component while preserving the equals contract, unless you are willing to forgo the benefits of object-oriented abstrac- tion. www.it-ebooks.info ITEM 8: OBEY THE GENERAL CONTRACT WHEN OVERRIDING EQUALS 39 You may hear it said that you can extend an instantiable class and add a value component while preserving the equals contract by using a getClass test in place of the instanceof test in the equals method: // Broken - violates Liskov substitution principle (page 40) @Override public boolean equals(Object o) { if (o == null || o.getClass() != getClass()) return false; Point p = (Point) o; return p.x == x && p.y == y; } This has the effect of equating objects only if they have the same implementation class. While this may not seem so bad, the consequences are unacceptable. Let’s suppose we want to write a method to tell whether an integer point is on the unit circle. Here is one way we could do it: // Initialize UnitCircle to contain all Points on the unit circle private static final Set unitCircle; static { unitCircle = new HashSet(); unitCircle.add(new Point( 1, 0)); unitCircle.add(new Point( 0, 1)); unitCircle.add(new Point(-1, 0)); unitCircle.add(new Point( 0, -1)); } public static boolean onUnitCircle(Point p) { return unitCircle.contains(p); } While this may not be the fastest way to implement the functionality, it works fine. But suppose you extend Point in some trivial way that doesn’t add a value com- ponent, say, by having its constructor keep track of how many instances have been created: public class CounterPoint extends Point { private static final AtomicInteger counter = new AtomicInteger(); public CounterPoint(int x, int y) { super(x, y); counter.incrementAndGet(); } public int numberCreated() { return counter.get(); } } www.it-ebooks.info CHAPTER 3 METHODS COMMON TO ALL OBJECTS40 The Liskov substitution principle says that any important property of a type should also hold for its subtypes, so that any method written for the type should work equally well on its subtypes [Liskov87]. But suppose we pass a Counter- Point instance to the onUnitCircle method. If the Point class uses a getClass- based equals method, the onUnitCircle method will return false regardless of the CounterPoint instance’s x and y values. This is so because collections, such as the HashSet used by the onUnitCircle method, use the equals method to test for containment, and no CounterPoint instance is equal to any Point. If, how- ever, you use a proper instanceof-based equals method on Point, the same onUnitCircle method will work fine when presented with a CounterPoint. While there is no satisfactory way to extend an instantiable class and add a value component, there is a fine workaround. Follow the advice of Item 16, “Favor composition over inheritance.” Instead of having ColorPoint extend Point, give ColorPoint a private Point field and a public view method (Item 5) that returns the point at the same position as this color point: // Adds a value component without violating the equals contract public class ColorPoint { private final Point point; private final Color color; public ColorPoint(int x, int y, Color color) { if (color == null) throw new NullPointerException(); point = new Point(x, y); this.color = color; } /** * Returns the point-view of this color point. */ public Point asPoint() { return point; } @Override public boolean equals(Object o) { if (!(o instanceof ColorPoint)) return false; ColorPoint cp = (ColorPoint) o; return cp.point.equals(point) && cp.color.equals(color); } ... // Remainder omitted } www.it-ebooks.info ITEM 8: OBEY THE GENERAL CONTRACT WHEN OVERRIDING EQUALS 41 There are some classes in the Java platform libraries that do extend an instan- tiable class and add a value component. For example, java.sql.Timestamp extends java.util.Date and adds a nanoseconds field. The equals implementa- tion for Timestamp does violate symmetry and can cause erratic behavior if Timestamp and Date objects are used in the same collection or are otherwise inter- mixed. The Timestamp class has a disclaimer cautioning programmers against mixing dates and timestamps. While you won’t get into trouble as long as you keep them separate, there’s nothing to prevent you from mixing them, and the resulting errors can be hard to debug. This behavior of the Timestamp class was a mistake and should not be emulated. Note that you can add a value component to a subclass of an abstract class without violating the equals contract. This is important for the sort of class hier- archies that you get by following the advice in Item 20, “Prefer class hierarchies to tagged classes.” For example, you could have an abstract class Shape with no value components, a subclass Circle that adds a radius field, and a subclass Rectangle that adds length and width fields. Problems of the sort shown above won’t occur so long as it is impossible to create a superclass instance directly. Consistency—The fourth requirement of the equals contract says that if two objects are equal, they must remain equal for all time unless one (or both) of them is modified. In other words, mutable objects can be equal to different objects at different times while immutable objects can’t. When you write a class, think hard about whether it should be immutable (Item 15). If you conclude that it should, make sure that your equals method enforces the restriction that equal objects remain equal and unequal objects remain unequal for all time. Whether or not a class is immutable, do not write an equals method that depends on unreliable resources. It’s extremely difficult to satisfy the consis- tency requirement if you violate this prohibition. For example, java.net.URL’s equals method relies on comparison of the IP addresses of the hosts associated with the URLs. Translating a host name to an IP address can require network access, and it isn’t guaranteed to yield the same results over time. This can cause the URL equals method to violate the equals contract and has caused problems in practice. (Unfortunately, this behavior cannot be changed due to compatibility requirements.) With very few exceptions, equals methods should perform deter- ministic computations on memory-resident objects. “Non-nullity”—The final requirement, which in the absence of a name I have taken the liberty of calling “non-nullity,” says that all objects must be unequal to null. While it is hard to imagine accidentally returning true in response to the invocation o.equals(null), it isn’t hard to imagine accidentally throwing a www.it-ebooks.info CHAPTER 3 METHODS COMMON TO ALL OBJECTS42 NullPointerException. The general contract does not allow this. Many classes have equals methods that guard against this with an explicit test for null: @Override public boolean equals(Object o) { if (o == null) return false; ... } This test is unnecessary. To test its argument for equality, the equals method must first cast its argument to an appropriate type so its accessors may be invoked or its fields accessed. Before doing the cast, the method must use the instanceof oper- ator to check that its argument is of the correct type: @Override public boolean equals(Object o) { if (!(o instanceof MyType)) return false; MyType mt = (MyType) o; ... } If this type check were missing and the equals method were passed an argument of the wrong type, the equals method would throw a ClassCastException, which violates the equals contract. But the instanceof operator is specified to return false if its first operand is null, regardless of what type appears in the sec- ond operand [JLS, 15.20.2]. Therefore the type check will return false if null is passed in, so you don’t need a separate null check. Putting it all together, here’s a recipe for a high-quality equals method: 1. Use the == operator to check if the argument is a reference to this object. If so, return true. This is just a performance optimization, but one that is worth doing if the comparison is potentially expensive. 2. Use the instanceof operator to check if the argument has the correct type. If not, return false. Typically, the correct type is the class in which the method occurs. Occasionally, it is some interface implemented by this class. Use an in- terface if the class implements an interface that refines the equals contract to permit comparisons across classes that implement the interface. Collection in- terfaces such as Set, List, Map, and Map.Entry have this property. 3. Cast the argument to the correct type. Because this cast was preceded by an instanceof test, it is guaranteed to succeed. www.it-ebooks.info ITEM 8: OBEY THE GENERAL CONTRACT WHEN OVERRIDING EQUALS 43 4. For each “significant” field in the class, check if that field of the argument matches the corresponding field of this object. If all these tests succeed, re- turn true; otherwise, return false. If the type in step 2 is an interface, you must access the argument’s fields via interface methods; if the type is a class, you may be able to access the fields directly, depending on their accessibility. For primitive fields whose type is not float or double, use the == operator for comparisons; for object reference fields, invoke the equals method recursive- ly; for float fields, use the Float.compare method; and for double fields, use Double.compare. The special treatment of float and double fields is made necessary by the existence of Float.NaN, -0.0f and the analogous double constants; see the Float.equals documentation for details. For array fields, apply these guidelines to each element. If every element in an array field is sig- nificant, you can use one of the Arrays.equals methods added in release 1.5. Some object reference fields may legitimately contain null. To avoid the pos- sibility of a NullPointerException, use this idiom to compare such fields: (field == null ? o.field == null : field.equals(o.field)) This alternative may be faster if field and o.field are often identical: (field == o.field || (field != null && field.equals(o.field))) For some classes, such as CaseInsensitiveString above, field comparisons are more complex than simple equality tests. If this is the case, you may want to store a canonical form of the field, so the equals method can do cheap exact comparisons on these canonical forms rather than more costly inexact compar- isons. This technique is most appropriate for immutable classes (Item 15); if the object can change, you must keep the canonical form up to date. The performance of the equals method may be affected by the order in which fields are compared. For best performance, you should first compare fields that are more likely to differ, less expensive to compare, or, ideally, both. You must not compare fields that are not part of an object’s logical state, such as Lock fields used to synchronize operations. You need not compare redundant fields, which can be calculated from “significant fields,” but doing so may improve the performance of the equals method. If a redundant field amounts to a sum- mary description of the entire object, comparing this field will save you the ex- pense of comparing the actual data if the comparison fails. For example, suppose you have a Polygon class, and you cache the area. If two polygons have unequal areas, you needn’t bother comparing their edges and vertices. www.it-ebooks.info CHAPTER 3 METHODS COMMON TO ALL OBJECTS44 5. When you are finished writing your equals method, ask yourself three questions: Is it symmetric? Is it transitive? Is it consistent? And don’t just ask yourself; write unit tests to check that these properties hold! If they don’t, figure out why not, and modify the equals method accordingly. Of course your equals method also has to satisfy the other two properties (reflexivity and “non-nullity”), but these two usually take care of themselves. For a concrete example of an equals method constructed according to the above recipe, see PhoneNumber.equals in Item 9. Here are a few final caveats: • Always override hashCode when you override equals (Item 9). • Don’t try to be too clever. If you simply test fields for equality, it’s not hard to adhere to the equals contract. If you are overly aggressive in searching for equivalence, it’s easy to get into trouble. It is generally a bad idea to take any form of aliasing into account. For example, the File class shouldn’t attempt to equate symbolic links referring to the same file. Thankfully, it doesn’t. • Don’t substitute another type for Object in the equals declaration. It is not uncommon for a programmer to write an equals method that looks like this, and then spend hours puzzling over why it doesn’t work properly: public boolean equals(MyClass o) { ... } The problem is that this method does not override Object.equals, whose ar- gument is of type Object, but overloads it instead (Item 41). It is acceptable to provide such a “strongly typed” equals method in addition to the normal one as long as the two methods return the same result, but there is no compelling reason to do so. It may provide minor performance gains under certain circum- stances, but it isn’t worth the added complexity (Item 55). Consistent use of the @Override annotation, as illustrated throughout this item, will prevent you from making this mistake (Item 36). This equals method won’t compile and the error message will tell you exactly what is wrong: @Override public boolean equals(MyClass o) { ... } www.it-ebooks.info ITEM 9: ALWAYS OVERRIDE HASHCODE WHEN YOU OVERRIDE EQUALS 45 Item 9: Always override hashCode when you override equals A common source of bugs is the failure to override the hashCode method. You must override hashCode in every class that overrides equals. Failure to do so will result in a violation of the general contract for Object.hashCode, which will prevent your class from functioning properly in conjunction with all hash-based collections, including HashMap, HashSet, and Hashtable. Here is the contract, copied from the Object specification [JavaSE6]: • Whenever it is invoked on the same object more than once during an execu- tion of an application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execu- tion of an application to another execution of the same application. • If two objects are equal according to the equals(Object) method, then call- ing the hashCode method on each of the two objects must produce the same integer result. • It is not required that if two objects are unequal according to the equals(Ob- ject) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables. The key provision that is violated when you fail to override hashCode is the second one: equal objects must have equal hash codes. Two distinct instances may be logically equal according to a class’s equals method, but to Object’s hashCode method, they’re just two objects with nothing much in com- mon. Therefore Object’s hashCode method returns two seemingly random num- bers instead of two equal numbers as required by the contract. For example, consider the following simplistic PhoneNumber class, whose equals method is constructed according to the recipe in Item 8: public final class PhoneNumber { private final short areaCode; private final short prefix; private final short lineNumber; public PhoneNumber(int areaCode, int prefix, int lineNumber) { rangeCheck(areaCode, 999, "area code"); rangeCheck(prefix, 999, "prefix"); rangeCheck(lineNumber, 9999, "line number"); www.it-ebooks.info CHAPTER 3 METHODS COMMON TO ALL OBJECTS46 this.areaCode = (short) areaCode; this.prefix = (short) prefix; this.lineNumber = (short) lineNumber; } private static void rangeCheck(int arg, int max, String name) { if (arg < 0 || arg > max) throw new IllegalArgumentException(name +": " + arg); } @Override public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof PhoneNumber)) return false; PhoneNumber pn = (PhoneNumber)o; return pn.lineNumber == lineNumber && pn.prefix == prefix && pn.areaCode == areaCode; } // Broken - no hashCode method! ... // Remainder omitted } Suppose you attempt to use this class with a HashMap: Map m = new HashMap(); m.put(new PhoneNumber(707, 867, 5309), "Jenny"); At this point, you might expect m.get(new PhoneNumber(707, 867, 5309)) to return "Jenny", but it returns null. Notice that two PhoneNumber instances are involved: one is used for insertion into the HashMap, and a second, equal, instance is used for (attempted) retrieval. The PhoneNumber class’s failure to override hashCode causes the two equal instances to have unequal hash codes, in violation of the hashCode contract. Therefore the get method is likely to look for the phone number in a different hash bucket from the one in which it was stored by the put method. Even if the two instances happen to hash to the same bucket, the get method will almost certainly return null, as HashMap has an optimization that caches the hash code associated with each entry and doesn’t bother checking for object equality if the hash codes don’t match. www.it-ebooks.info ITEM 9: ALWAYS OVERRIDE HASHCODE WHEN YOU OVERRIDE EQUALS 47 Fixing this problem is as simple as providing a proper hashCode method for the PhoneNumber class. So what should a hashCode method look like? It’s trivial to write one that is legal but not good. This one, for example, is always legal but should never be used: // The worst possible legal hash function - never use! @Override public int hashCode() { return 42; } It’s legal because it ensures that equal objects have the same hash code. It’s atrocious because it ensures that every object has the same hash code. Therefore, every object hashes to the same bucket, and hash tables degenerate to linked lists. Programs that should run in linear time instead run in quadratic time. For large hash tables, this is the difference between working and not working. A good hash function tends to produce unequal hash codes for unequal objects. This is exactly what is meant by the third provision of the hashCode con- tract. Ideally, a hash function should distribute any reasonable collection of unequal instances uniformly across all possible hash values. Achieving this ideal can be difficult. Luckily it’s not too difficult to achieve a fair approximation. Here is a simple recipe: 1. Store some constant nonzero value, say, 17, in an int variable called result. 2. For each significant field f in your object (each field taken into account by the equals method, that is), do the following: a. Compute an int hash code c for the field: i. If the field is a boolean, compute (f?1:0). ii. If the field is a byte, char, short, or int, compute (int) f. iii. If the field is a long, compute (int) (f ^ (f >>> 32)). iv. If the field is a float, compute Float.floatToIntBits(f). v. If the field is a double, compute Double.doubleToLongBits(f), and then hash the resulting long as in step 2.a.iii. vi. If the field is an object reference and this class’s equals method compares the field by recursively invoking equals, recursively invoke hashCode on the field. If a more complex comparison is required, compute a “canonical representation” for this field and invoke hashCode on the canonical representation. If the value of the field is null, return 0 (or some other constant, but 0 is traditional). www.it-ebooks.info CHAPTER 3 METHODS COMMON TO ALL OBJECTS48 vii. If the field is an array, treat it as if each element were a separate field. That is, compute a hash code for each significant element by applying these rules recursively, and combine these values per step 2.b. If every element in an array field is significant, you can use one of the Arrays.hashCode methods added in release 1.5. b. Combine the hash code c computed in step 2.a into result as follows: result = 31 * result + c; 3. Return result. 4. When you are finished writing the hashCode method, ask yourself whether equal instances have equal hash codes. Write unit tests to verify your intuition! If equal instances have unequal hash codes, figure out why and fix the problem. You may exclude redundant fields from the hash code computation. In other words, you may ignore any field whose value can be computed from fields included in the computation. You must exclude any fields that are not used in equals com- parisons, or you risk violating the second provision of the hashCode contract. A nonzero initial value is used in step 1 so the hash value will be affected by initial fields whose hash value, as computed in step 2.a, is zero. If zero were used as the initial value in step 1, the overall hash value would be unaffected by any such initial fields, which could increase collisions. The value 17 is arbitrary. The multiplication in step 2.b makes the result depend on the order of the fields, yielding a much better hash function if the class has multiple similar fields. For example, if the multiplication were omitted from a String hash function, all anagrams would have identical hash codes. The value 31 was chosen because it is an odd prime. If it were even and the multiplication overflowed, information would be lost, as multiplication by 2 is equivalent to shifting. The advantage of using a prime is less clear, but it is traditional. A nice property of 31 is that the multiplication can be replaced by a shift and a subtraction for better performance: 31*i==(i<<5)-i. Modern VMs do this sort of optimization automatically. Let’s apply the above recipe to the PhoneNumber class. There are three signif- icant fields, all of type short: @Override public int hashCode() { int result = 17; result = 31 * result + areaCode; result = 31 * result + prefix; result = 31 * result + lineNumber; return result; } www.it-ebooks.info ITEM 9: ALWAYS OVERRIDE HASHCODE WHEN YOU OVERRIDE EQUALS 49 Because this method returns the result of a simple deterministic computation whose only inputs are the three significant fields in a PhoneNumber instance, it is clear that equal PhoneNumber instances have equal hash codes. This method is, in fact, a perfectly good hashCode implementation for PhoneNumber, on a par with those in the Java platform libraries. It is simple, reasonably fast, and does a rea- sonable job of dispersing unequal phone numbers into different hash buckets. If a class is immutable and the cost of computing the hash code is significant, you might consider caching the hash code in the object rather than recalculating it each time it is requested. If you believe that most objects of this type will be used as hash keys, then you should calculate the hash code when the instance is created. Otherwise, you might choose to lazily initialize it the first time hashCode is invoked (Item 71). It is not clear that our PhoneNumber class merits this treatment, but just to show you how it’s done: // Lazily initialized, cached hashCode private volatile int hashCode; // (See Item 71) @Override public int hashCode() { int result = hashCode; if (result == 0) { result = 17; result = 31 * result + areaCode; result = 31 * result + prefix; result = 31 * result + lineNumber; hashCode = result; } return result; } While the recipe in this item yields reasonably good hash functions, it does not yield state-of-the-art hash functions, nor do the Java platform libraries provide such hash functions as of release 1.6. Writing such hash functions is a research topic, best left to mathematicians and theoretical computer scientists. Perhaps a later release of the platform will provide state-of-the-art hash functions for its classes and utility methods to allow average programmers to construct such hash functions. In the meantime, the techniques described in this item should be ade- quate for most applications. Do not be tempted to exclude significant parts of an object from the hash code computation to improve performance. While the resulting hash function may run faster, its poor quality may degrade hash tables’ performance to the point where they become unusably slow. In particular, the hash function may, in prac- www.it-ebooks.info CHAPTER 3 METHODS COMMON TO ALL OBJECTS50 tice, be confronted with a large collection of instances that differ largely in the regions that you’ve chosen to ignore. If this happens, the hash function will map all the instances to a very few hash codes, and hash-based collections will display quadratic performance. This is not just a theoretical problem. The String hash function implemented in all releases prior to 1.2 examined at most sixteen charac- ters, evenly spaced throughout the string, starting with the first character. For large collections of hierarchical names, such as URLs, this hash function displayed exactly the pathological behavior noted here. Many classes in the Java platform libraries, such as String, Integer, and Date, include in their specifications the exact value returned by their hashCode method as a function of the instance value. This is generally not a good idea, as it severely limits your ability to improve the hash function in future releases. If you leave the details of a hash function unspecified and a flaw is found or a better hash function discovered, you can change the hash function in a subsequent release, confident that no clients depend on the exact values returned by the hash function. www.it-ebooks.info ITEM 10: ALWAYS OVERRIDE TOSTRING 51 Item 10: Always override toString While java.lang.Object provides an implementation of the toString method, the string that it returns is generally not what the user of your class wants to see. It consists of the class name followed by an “at” sign (@) and the unsigned hexadeci- mal representation of the hash code, for example, “PhoneNumber@163b91.” The general contract for toString says that the returned string should be “a concise but informative representation that is easy for a person to read” [JavaSE6]. While it could be argued that “PhoneNumber@163b91” is concise and easy to read, it isn’t very informative when compared to “(707) 867-5309.” The toString contract goes on to say, “It is recommended that all subclasses override this method.” Good advice, indeed! While it isn’t as important as obeying the equals and hashCode contracts (Item 8, Item 9), providing a good toString implementation makes your class much more pleasant to use. The toString method is automatically invoked when an object is passed to println, printf, the string concatenation operator, or assert, or printed by a debugger. (The printf method was added to the platform in release 1.5, as were related methods including String.format, which is roughly equivalent to C’s sprintf.) If you’ve provided a good toString method for PhoneNumber, generating a useful diagnostic message is as easy as this: System.out.println("Failed to connect: " + phoneNumber); Programmers will generate diagnostic messages in this fashion whether or not you override toString, but the messages won’t be useful unless you do. The ben- efits of providing a good toString method extend beyond instances of the class to objects containing references to these instances, especially collections. Which would you rather see when printing a map, “{Jenny=PhoneNumber@163b91}” or “{Jenny=(707) 867-5309}”? When practical, the toString method should return all of the interesting information contained in the object, as in the phone number example just shown. It is impractical if the object is large or if it contains state that is not condu- cive to string representation. Under these circumstances, toString should return a summary such as “Manhattan white pages (1487536 listings)” or “Thread[main,5,main]”. Ideally, the string should be self-explanatory. (The Thread example flunks this test.) www.it-ebooks.info CHAPTER 3 METHODS COMMON TO ALL OBJECTS52 One important decision you’ll have to make when implementing a toString method is whether to specify the format of the return value in the documentation. It is recommended that you do this for value classes, such as phone numbers or matrices. The advantage of specifying the format is that it serves as a standard, unambiguous, human-readable representation of the object. This representation can be used for input and output and in persistent human-readable data objects, such as XML documents. If you specify the format, it’s usually a good idea to pro- vide a matching static factory or constructor so programmers can easily translate back and forth between the object and its string representation. This approach is taken by many value classes in the Java platform libraries, including BigInteger, BigDecimal, and most of the boxed primitive classes. The disadvantage of specifying the format of the toString return value is that once you’ve specified it, you’re stuck with it for life, assuming your class is widely used. Programmers will write code to parse the representation, to generate it, and to embed it into persistent data. If you change the representation in a future release, you’ll break their code and data, and they will yowl. By failing to specify a format, you preserve the flexibility to add information or improve the format in a subsequent release. Whether or not you decide to specify the format, you should clearly docu- ment your intentions. If you specify the format, you should do so precisely. For example, here’s a toString method to go with the PhoneNumber class in Item 9: /** * Returns the string representation of this phone number. * The string consists of fourteen characters whose format * is "(XXX) YYY-ZZZZ", where XXX is the area code, YYY is * the prefix, and ZZZZ is the line number. (Each of the * capital letters represents a single decimal digit.) * * If any of the three parts of this phone number is too small * to fill up its field, the field is padded with leading zeros. * For example, if the value of the line number is 123, the last * four characters of the string representation will be "0123". * * Note that there is a single space separating the closing * parenthesis after the area code from the first digit of the * prefix. */ @Override public String toString() { return String.format("(%03d) %03d-%04d", areaCode, prefix, lineNumber); } www.it-ebooks.info ITEM 10: ALWAYS OVERRIDE TOSTRING 53 If you decide not to specify a format, the documentation comment should read something like this: /** * Returns a brief description of this potion. The exact details * of the representation are unspecified and subject to change, * but the following may be regarded as typical: * * "[Potion #9: type=love, smell=turpentine, look=india ink]" */ @Override public String toString() { ... } After reading this comment, programmers who produce code or persistent data that depends on the details of the format will have no one but themselves to blame when the format is changed. Whether or not you specify the format, provide programmatic access to all of the information contained in the value returned by toString. For example, the PhoneNumber class should contain accessors for the area code, prefix, and line number. If you fail to do this, you force programmers who need this information to parse the string. Besides reducing performance and making unnecessary work for programmers, this process is error-prone and results in fragile systems that break if you change the format. By failing to provide accessors, you turn the string format into a de facto API, even if you’ve specified that it’s subject to change. www.it-ebooks.info CHAPTER 3 METHODS COMMON TO ALL OBJECTS54 Item 11: Override clone judiciously The Cloneable interface was intended as a mixin interface (Item 18) for objects to advertise that they permit cloning. Unfortunately, it fails to serve this purpose. Its primary flaw is that it lacks a clone method, and Object’s clone method is pro- tected. You cannot, without resorting to reflection (Item 53), invoke the clone method on an object merely because it implements Cloneable. Even a reflective invocation may fail, as there is no guarantee that the object has an accessible clone method. Despite this flaw and others, the facility is in wide use so it pays to understand it. This item tells you how to implement a well-behaved clone method, discusses when it is appropriate to do so, and presents alternatives. So what does Cloneable do, given that it contains no methods? It determines the behavior of Object’s protected clone implementation: if a class implements Cloneable, Object’s clone method returns a field-by-field copy of the object; otherwise it throws CloneNotSupportedException. This is a highly atypical use of interfaces and not one to be emulated. Normally, implementing an interface says something about what a class can do for its clients. In the case of Cloneable, it modifies the behavior of a protected method on a superclass. If implementing the Cloneable interface is to have any effect on a class, the class and all of its superclasses must obey a fairly complex, unenforceable, and thinly documented protocol. The resulting mechanism is extralinguistic: it creates an object without calling a constructor. The general contract for the clone method is weak. Here it is, copied from the specification for java.lang.Object [JavaSE6]: Creates and returns a copy of this object. The precise meaning of “copy” may depend on the class of the object. The general intent is that, for any object x, the expression x.clone() != x will be true, and the expression x.clone().getClass() == x.getClass() will be true, but these are not absolute requirements. While it is typically the case that x.clone().equals(x) will be true, this is not an absolute requirement. Copying an object will typi- cally entail creating a new instance of its class, but it may require copying of internal data structures as well. No constructors are called. www.it-ebooks.info ITEM 11: OVERRIDE CLONE JUDICIOUSLY 55 There are a number of problems with this contract. The provision that “no constructors are called” is too strong. A well-behaved clone method can call con- structors to create objects internal to the clone under construction. If the class is final, clone can even return an object created by a constructor. The provision that x.clone().getClass() should generally be identical to x.getClass(), however, is too weak. In practice, programmers assume that if they extend a class and invoke super.clone from the subclass, the returned object will be an instance of the subclass. The only way a superclass can provide this functionality is to return an object obtained by calling super.clone. If a clone method returns an object created by a constructor, it will have the wrong class. Therefore, if you override the clone method in a nonfinal class, you should return an object obtained by invoking super.clone. If all of a class’s super- classes obey this rule, then invoking super.clone will eventually invoke Object’s clone method, creating an instance of the right class. This mechanism is vaguely similar to automatic constructor chaining, except that it isn’t enforced. The Cloneable interface does not, as of release 1.6, spell out in detail the responsibilities that a class takes on when it implements this interface. In prac- tice, a class that implements Cloneable is expected to provide a properly functioning public clone method. It is not, in general, possible to do so unless all of the class’s superclasses provide a well-behaved clone implementation, whether public or protected. Suppose you want to implement Cloneable in a class whose superclasses pro- vide well-behaved clone methods. The object you get from super.clone() may or may not be close to what you’ll eventually return, depending on the nature of the class. This object will be, from the standpoint of each superclass, a fully func- tional clone of the original object. The fields declared in your class (if any) will have values identical to those of the object being cloned. If every field contains a primitive value or a reference to an immutable object, the returned object may be exactly what you need, in which case no further processing is necessary. This is the case, for example, for the PhoneNumber class in Item 9. In this case, all you need do in addition to declaring that you implement Cloneable is to provide pub- lic access to Object’s protected clone method: @Override public PhoneNumber clone() { try { return (PhoneNumber) super.clone(); } catch(CloneNotSupportedException e) { throw new AssertionError(); // Can't happen } } www.it-ebooks.info CHAPTER 3 METHODS COMMON TO ALL OBJECTS56 Note that the above clone method returns PhoneNumber, not Object. As of release 1.5, it is legal and desirable to do this, because covariant return types were introduced in release 1.5 as part of generics. In other words, it is now legal for an overriding method’s return type to be a subclass of the overridden method’s return type. This allows the overriding method to provide more information about the returned object and eliminates the need for casting in the client. Because Object.clone returns Object, PhoneNumber.clone must cast the result of super.clone() before returning it, but this is far preferable to requiring every caller of PhoneNumber.clone to cast the result. The general principle at play here is never make the client do anything the library can do for the client. If an object contains fields that refer to mutable objects, using the simple clone implementation shown above can be disastrous. For example, consider the Stack class in Item 6: public class Stack { private Object[] elements; private int size = 0; private static final int DEFAULT_INITIAL_CAPACITY = 16; public Stack() { this.elements = new Object[DEFAULT_INITIAL_CAPACITY]; } public void push(Object e) { ensureCapacity(); elements[size++] = e; } public Object pop() { if (size == 0) throw new EmptyStackException(); Object result = elements[--size]; elements[size] = null; // Eliminate obsolete reference return result; } // Ensure space for at least one more element. private void ensureCapacity() { if (elements.length == size) elements = Arrays.copyOf(elements, 2 * size + 1); } } Suppose you want to make this class cloneable. If its clone method merely returns super.clone(), the resulting Stack instance will have the correct value in www.it-ebooks.info ITEM 11: OVERRIDE CLONE JUDICIOUSLY 57 its size field, but its elements field will refer to the same array as the original Stack instance. Modifying the original will destroy the invariants in the clone and vice versa. You will quickly find that your program produces nonsensical results or throws a NullPointerException. This situation could never occur as a result of calling the sole constructor in the Stack class. In effect, the clone method functions as another constructor; you must ensure that it does no harm to the original object and that it prop- erly establishes invariants on the clone. In order for the clone method on Stack to work properly, it must copy the internals of the stack. The easiest way to do this is to call clone recursively on the elements array: @Override public Stack clone() { try { Stack result = (Stack) super.clone(); result.elements = elements.clone(); return result; } catch (CloneNotSupportedException e) { throw new AssertionError(); } } Note that we do not have to cast the result of elements.clone() to Object[]. As of release 1.5, calling clone on an array returns an array whose compile-time type is the same as that of the array being cloned. Note also that the above solution would not work if the elements field were final, because clone would be prohibited from assigning a new value to the field. This is a fundamental problem: the clone architecture is incompatible with normal use of final fields referring to mutable objects, except in cases where the mutable objects may be safely shared between an object and its clone. In order to make a class cloneable, it may be necessary to remove final modifiers from some fields. It is not always sufficient to call clone recursively. For example, suppose you are writing a clone method for a hash table whose internals consist of an array of buckets, each of which references the first entry in a linked list of key-value pairs or is null if the bucket is empty. For performance, the class implements its own lightweight singly linked list instead of using java.util.LinkedList internally: public class HashTable implements Cloneable { private Entry[] buckets = ...; www.it-ebooks.info CHAPTER 3 METHODS COMMON TO ALL OBJECTS58 private static class Entry { final Object key; Object value; Entry next; Entry(Object key, Object value, Entry next) { this.key = key; this.value = value; this.next = next; } } ... // Remainder omitted } Suppose you merely clone the bucket array recursively, as we did for Stack: // Broken - results in shared internal state! @Override public HashTable clone() { try { HashTable result = (HashTable) super.clone(); result.buckets = buckets.clone(); return result; } catch (CloneNotSupportedException e) { throw new AssertionError(); } } Though the clone has its own bucket array, this array references the same linked lists as the original, which can easily cause nondeterministic behavior in both the clone and the original. To fix this problem, you’ll have to copy the linked list that comprises each bucket individually. Here is one common approach: public class HashTable implements Cloneable { private Entry[] buckets = ...; private static class Entry { final Object key; Object value; Entry next; Entry(Object key, Object value, Entry next) { this.key = key; this.value = value; this.next = next; } www.it-ebooks.info ITEM 11: OVERRIDE CLONE JUDICIOUSLY 59 // Recursively copy the linked list headed by this Entry Entry deepCopy() { return new Entry(key, value, next == null ? null : next.deepCopy()); } } @Override public HashTable clone() { try { HashTable result = (HashTable) super.clone(); result.buckets = new Entry[buckets.length]; for (int i = 0; i < buckets.length; i++) if (buckets[i] != null) result.buckets[i] = buckets[i].deepCopy(); return result; } catch (CloneNotSupportedException e) { throw new AssertionError(); } } ... // Remainder omitted } The private class HashTable.Entry has been augmented to support a “deep copy” method. The clone method on HashTable allocates a new buckets array of the proper size and iterates over the original buckets array, deep-copying each nonempty bucket. The deep-copy method on Entry invokes itself recursively to copy the entire linked list headed by the entry. While this technique is cute and works fine if the buckets aren’t too long, it is not a good way to clone a linked list because it consumes one stack frame for each element in the list. If the list is long, this could easily cause a stack overflow. To prevent this from happening, you can replace the recursion in deepCopy with iteration: // Iteratively copy the linked list headed by this Entry Entry deepCopy() { Entry result = new Entry(key, value, next); for (Entry p = result; p.next != null; p = p.next) p.next = new Entry(p.next.key, p.next.value, p.next.next); return result; } A final approach to cloning complex objects is to call super.clone, set all of the fields in the resulting object to their virgin state, and then call higher-level methods to regenerate the state of the object. In the case of our HashTable exam- www.it-ebooks.info CHAPTER 3 METHODS COMMON TO ALL OBJECTS60 ple, the buckets field would be initialized to a new bucket array, and the put(key, value) method (not shown) would be invoked for each key-value map- ping in the hash table being cloned. This approach typically yields a simple, rea- sonably elegant clone method that generally doesn’t run quite as fast as one that directly manipulates the innards of the object and its clone. Like a constructor, a clone method should not invoke any nonfinal methods on the clone under construction (Item 17). If clone invokes an overridden method, this method will execute before the subclass in which it is defined has had a chance to fix its state in the clone, quite possibly leading to corruption in the clone and the original. Therefore the put(key, value) method discussed in the previ- ous paragraph should be either final or private. (If it is private, it is presumably the “helper method” for a nonfinal public method.) Object’s clone method is declared to throw CloneNotSupportedException, but overriding clone methods can omit this declaration. Public clone methods should omit it because methods that don’t throw checked exceptions are easier to use (Item 59). If a class that is designed for inheritance (Item 17) overrides clone, the overriding method should mimic the behavior of Object.clone: it should be declared protected, it should be declared to throw CloneNotSupportedExcep- tion, and the class should not implement Cloneable. This gives subclasses the freedom to implement Cloneable or not, just as if they extended Object directly. One more detail bears noting. If you decide to make a thread-safe class imple- ment Cloneable, remember that its clone method must be properly synchronized just like any other method (Item 66). Object’s clone method is not synchronized, so even if it is otherwise satisfactory, you may have to write a synchronized clone method that invokes super.clone(). To recap, all classes that implement Cloneable should override clone with a public method whose return type is the class itself. This method should first call super.clone and then fix any fields that need to be fixed. Typically, this means copying any mutable objects that comprise the internal “deep structure” of the object being cloned, and replacing the clone’s references to these objects with ref- erences to the copies. While these internal copies can generally be made by call- ing clone recursively, this is not always the best approach. If the class contains only primitive fields or references to immutable objects, then it is probably the case that no fields need to be fixed. There are exceptions to this rule. For example, a field representing a serial number or other unique ID or a field representing the object’s creation time will need to be fixed, even if it is primitive or immutable. Is all this complexity really necessary? Rarely. If you extend a class that implements Cloneable, you have little choice but to implement a well-behaved www.it-ebooks.info ITEM 11: OVERRIDE CLONE JUDICIOUSLY 61 clone method. Otherwise, you are better off providing an alternative means of object copying, or simply not providing the capability. For example, it doesn’t make sense for immutable classes to support object copying, because copies would be virtually indistinguishable from the original. A fine approach to object copying is to provide a copy constructor or copy factory. A copy constructor is simply a constructor that takes a single argument whose type is the class containing the constructor, for example, public Yum(Yum yum); A copy factory is the static factory analog of a copy constructor: public static Yum newInstance(Yum yum); The copy constructor approach and its static factory variant have many advantages over Cloneable/clone: they don’t rely on a risk-prone extralinguistic object creation mechanism; they don’t demand unenforceable adherence to thinly documented conventions; they don’t conflict with the proper use of final fields; they don’t throw unnecessary checked exceptions; and they don’t require casts. While it is impossible to put a copy constructor or factory in an interface, Cloneable fails to function as an interface because it lacks a public clone method. Therefore you aren’t giving up interface functionality by using a copy constructor or factory in preference to a clone method. Furthermore, a copy constructor or factory can take an argument whose type is an interface implemented by the class. For example, by convention all general- purpose collection implementations provide a constructor whose argument is of type Collection or Map. Interface-based copy constructors and factories, more properly known as conversion constructors and conversion factories, allow the client to choose the implementation type of the copy rather than forcing the client to accept the implementation type of the original. Suppose you have a HashSet s, and you want to copy it as a TreeSet. The clone method can’t offer this function- ality, but it’s easy with a conversion constructor: new TreeSet(s). Given all of the problems associated with Cloneable, it’s safe to say that other interfaces should not extend it, and that classes designed for inheritance (Item 17) should not implement it. Because of its many shortcomings, some expert programmers simply choose never to override the clone method and never to invoke it except, perhaps, to copy arrays. If you design a class for inheritance, be aware that if you choose not to provide a well-behaved protected clone method, it will be impossible for subclasses to implement Cloneable. www.it-ebooks.info CHAPTER 3 METHODS COMMON TO ALL OBJECTS62 Item 12: Consider implementing Comparable Unlike the other methods discussed in this chapter, the compareTo method is not declared in Object. Rather, it is the sole method in the Comparable interface. It is similar in character to Object’s equals method, except that it permits order com- parisons in addition to simple equality comparisons, and it is generic. By imple- menting Comparable, a class indicates that its instances have a natural ordering. Sorting an array of objects that implement Comparable is as simple as this: Arrays.sort(a); It is similarly easy to search, compute extreme values, and maintain automati- cally sorted collections of Comparable objects. For example, the following pro- gram, which relies on the fact that String implements Comparable, prints an alphabetized list of its command-line arguments with duplicates eliminated: public class WordList { public static void main(String[] args) { Set s = new TreeSet(); Collections.addAll(s, args); System.out.println(s); } } By implementing Comparable, you allow your class to interoperate with all of the many generic algorithms and collection implementations that depend on this interface. You gain a tremendous amount of power for a small amount of effort. Virtually all of the value classes in the Java platform libraries implement Compa- rable. If you are writing a value class with an obvious natural ordering, such as alphabetical order, numerical order, or chronological order, you should strongly consider implementing the interface: public interface Comparable { int compareTo(T t); } The general contract of the compareTo method is similar to that of equals: Compares this object with the specified object for order. Returns a negative in- teger, zero, or a positive integer as this object is less than, equal to, or greater than the specified object. Throws ClassCastException if the specified ob- ject’s type prevents it from being compared to this object. www.it-ebooks.info ITEM 12: CONSIDER IMPLEMENTING COMPARABLE 63 In the following description, the notation sgn(expression) designates the math- ematical signum function, which is defined to return -1, 0, or 1, according to whether the value of expression is negative, zero, or positive. • The implementor must ensure sgn(x.compareTo(y)) == -sgn(y.compare- To(x)) for all x and y. (This implies that x.compareTo(y) must throw an ex- ception if and only if y.compareTo(x) throws an exception.) • The implementor must also ensure that the relation is transitive: (x.com- pareTo(y) > 0 && y.compareTo(z) > 0) implies x.compareTo(z) > 0. • Finally, the implementor must ensure that x.compareTo(y) == 0 implies that sgn(x.compareTo(z)) == sgn(y.compareTo(z)), for all z. • It is strongly recommended, but not strictly required, that (x.compareTo(y) == 0) == (x.equals(y)). Generally speaking, any class that implements the Comparable interface and violates this condition should clearly indicate this fact. The recommended language is “Note: This class has a natural ordering that is inconsistent with equals.” Don’t be put off by the mathematical nature of this contract. Like the equals contract (Item 8), this contract isn’t as complicated as it looks. Within a class, any reasonable ordering will satisfy it. Across classes, compareTo, unlike equals, doesn’t have to work: it is permitted to throw ClassCastException if two object references being compared refer to objects of different classes. Usually, that is exactly what compareTo should do, and what it will do if the class is properly parameterized. While the contract doesn’t preclude interclass comparisons, there are, as of release 1.6, no classes in the Java platform libraries that support them. Just as a class that violates the hashCode contract can break other classes that depend on hashing, a class that violates the compareTo contract can break other classes that depend on comparison. Classes that depend on comparison include the sorted collections TreeSet and TreeMap, and the utility classes Collections and Arrays, which contain searching and sorting algorithms. Let’s go over the provisions of the compareTo contract. The first provision says that if you reverse the direction of a comparison between two object refer- ences, the expected thing happens: if the first object is less than the second, then the second must be greater than the first; if the first object is equal to the second, then the second must be equal to the first; and if the first object is greater than the second, then the second must be less than the first. The second provision says that if one object is greater than a second, and the second is greater than a third, then the first must be greater than the third. The final provision says that all objects that compare as equal must yield the same results when compared to any other object. www.it-ebooks.info CHAPTER 3 METHODS COMMON TO ALL OBJECTS64 One consequence of these three provisions is that the equality test imposed by a compareTo method must obey the same restrictions imposed by the equals con- tract: reflexivity, symmetry, and transitivity. Therefore the same caveat applies: there is no way to extend an instantiable class with a new value component while preserving the compareTo contract, unless you are willing to forgo the benefits of object-oriented abstraction (Item 8). The same workaround applies, too. If you want to add a value component to a class that implements Comparable, don’t extend it; write an unrelated class containing an instance of the first class. Then provide a “view” method that returns this instance. This frees you to implement whatever compareTo method you like on the second class, while allowing its cli- ent to view an instance of the second class as an instance of the first class when needed. The final paragraph of the compareTo contract, which is a strong suggestion rather than a true provision, simply states that the equality test imposed by the compareTo method should generally return the same results as the equals method. If this provision is obeyed, the ordering imposed by the compareTo method is said to be consistent with equals. If it’s violated, the ordering is said to be inconsistent with equals. A class whose compareTo method imposes an order that is inconsistent with equals will still work, but sorted collections containing elements of the class may not obey the general contract of the appropriate collec- tion interfaces (Collection, Set, or Map). This is because the general contracts for these interfaces are defined in terms of the equals method, but sorted collec- tions use the equality test imposed by compareTo in place of equals. It is not a catastrophe if this happens, but it’s something to be aware of. For example, consider the BigDecimal class, whose compareTo method is inconsistent with equals. If you create a HashSet instance and add new BigDecimal("1.0") and new BigDecimal("1.00"), the set will contain two elements because the two BigDecimal instances added to the set are unequal when compared using the equals method. If, however, you perform the same procedure using a TreeSet instead of a HashSet, the set will contain only one element because the two BigDecimal instances are equal when compared using the compareTo method. (See the BigDecimal documentation for details.) Writing a compareTo method is similar to writing an equals method, but there are a few key differences. Because the Comparable interface is parameter- ized, the compareTo method is statically typed, so you don’t need to type check or cast its argument. If the argument is of the wrong type, the invocation won’t even compile. If the argument is null, the invocation should throw a NullPointerEx- ception, and it will, as soon as the method attempts to access its members. www.it-ebooks.info ITEM 12: CONSIDER IMPLEMENTING COMPARABLE 65 The field comparisons in a compareTo method are order comparisons rather than equality comparisons. Compare object reference fields by invoking the compareTo method recursively. If a field does not implement Comparable, or you need to use a nonstandard ordering, you can use an explicit Comparator instead. Either write your own, or use a preexisting one as in this compareTo method for the CaseInsensitiveString class in Item 8. public final class CaseInsensitiveString implements Comparable { public int compareTo(CaseInsensitiveString cis) { return String.CASE_INSENSITIVE_ORDER.compare(s, cis.s); } ... // Remainder omitted } Note that the CaseInsensitiveString class implements Compara- ble. This means that a CaseInsensitiveString ref- erence can be compared only to other CaseInsensitiveString references. It is the normal pattern to follow when declaring a class to implement Comparable. Note also that the parameter of the compareTo method is a CaseInsensitive- String, not an Object. This is required by the aforementioned class declaration. Compare integral primitive fields using the relational operators < and >. For floating-point fields, use Double.compare or Float.compare in place of the relational operators, which do not obey the general contract for compareTo when applied to floating point values. For array fields, apply these guidelines to each element. If a class has multiple significant fields, the order in which you compare them is critical. You must start with the most significant field and work your way down. If a comparison results in anything other than zero (which represents equality), you’re done; just return the result. If the most significant fields are equal, go on to compare the next-most-significant fields, and so on. If all fields are equal, the objects are equal; return zero. The technique is demonstrated by this compareTo method for the PhoneNumber class in Item 9: public int compareTo(PhoneNumber pn) { // Compare area codes if (areaCode < pn.areaCode) return -1; if (areaCode > pn.areaCode) return 1; www.it-ebooks.info CHAPTER 3 METHODS COMMON TO ALL OBJECTS66 // Area codes are equal, compare prefixes if (prefix < pn.prefix) return -1; if (prefix > pn.prefix) return 1; // Area codes and prefixes are equal, compare line numbers if (lineNumber < pn.lineNumber) return -1; if (lineNumber > pn.lineNumber) return 1; return 0; // All fields are equal } While this method works, it can be improved. Recall that the contract for com- pareTo does not specify the magnitude of the return value, only the sign. You can take advantage of this to simplify the code and probably make it run a bit faster: public int compareTo(PhoneNumber pn) { // Compare area codes int areaCodeDiff = areaCode - pn.areaCode; if (areaCodeDiff != 0) return areaCodeDiff; // Area codes are equal, compare prefixes int prefixDiff = prefix - pn.prefix; if (prefixDiff != 0) return prefixDiff; // Area codes and prefixes are equal, compare line numbers return lineNumber - pn.lineNumber; } This trick works fine here but should be used with extreme caution. Don’t use it unless you’re certain the fields in question are non-negative or, more generally, that the difference between the lowest and highest possible field values is less than or equal to Integer.MAX_VALUE (231-1). The reason this trick doesn’t always work is that a signed 32-bit integer isn’t big enough to hold the difference between two arbitrary signed 32-bit integers. If i is a large positive int and j is a large negative int, (i - j) will overflow and return a negative value. The resulting compareTo method will return incorrect results for some arguments and violate the first and second provisions of the compareTo contract. This is not a purely theoretical prob- lem: it has caused failures in real systems. These failures can be difficult to debug, as the broken compareTo method works properly for most input values. www.it-ebooks.info 67 CHAPTER 4 Classes and Interfaces CLASSES and interfaces lie at the heart of the Java programming language. They are its basic units of abstraction. The language provides many powerful ele- ments that you can use to design classes and interfaces. This chapter contains guidelines to help you make the best use of these elements so that your classes and interfaces are usable, robust, and flexible. Item 13: Minimize the accessibility of classes and members The single most important factor that distinguishes a well-designed module from a poorly designed one is the degree to which the module hides its internal data and other implementation details from other modules. A well-designed module hides all of its implementation details, cleanly separating its API from its implementa- tion. Modules then communicate only through their APIs and are oblivious to each others’ inner workings. This concept, known as information hiding or encap- sulation, is one of the fundamental tenets of software design [Parnas72]. Information hiding is important for many reasons, most of which stem from the fact that it decouples the modules that comprise a system, allowing them to be developed, tested, optimized, used, understood, and modified in isolation. This speeds up system development because modules can be developed in parallel. It eases the burden of maintenance because modules can be understood more quickly and debugged with little fear of harming other modules. While informa- tion hiding does not, in and of itself, cause good performance, it enables effective performance tuning: once a system is complete and profiling has determined which modules are causing performance problems (Item 55), those modules can be optimized without affecting the correctness of other modules. Information hid- ing increases software reuse because modules that aren’t tightly coupled often prove useful in other contexts besides the ones for which they were developed. www.it-ebooks.info CHAPTER 4 CLASSES AND INTERFACES68 Finally, information hiding decreases the risk in building large systems, because individual modules may prove successful even if the system does not. Java has many facilities to aid in information hiding. The access control mecha- nism [JLS, 6.6] specifies the accessibility of classes, interfaces, and members. The accessibility of an entity is determined by the location of its declaration and by which, if any, of the access modifiers (private, protected, and public) is present on the declaration. Proper use of these modifiers is essential to information hiding. The rule of thumb is simple: make each class or member as inaccessible as possible. In other words, use the lowest possible access level consistent with the proper functioning of the software that you are writing. For top-level (non-nested) classes and interfaces, there are only two possible access levels: package-private and public. If you declare a top-level class or inter- face with the public modifier, it will be public; otherwise, it will be package-pri- vate. If a top-level class or interface can be made package-private, it should be. By making it package-private, you make it part of the implementation rather than the exported API, and you can modify it, replace it, or eliminate it in a subsequent release without fear of harming existing clients. If you make it public, you are obligated to support it forever to maintain compatibility. If a package-private top-level class (or interface) is used by only one class, consider making the top-level class a private nested class of the sole class that uses it (Item 22). This reduces its accessibility from all the classes in its package to the one class that uses it. But it is far more important to reduce the accessibility of a gratuitously public class than of a package-private top-level class: the public class is part of the package’s API, while the package-private top-level class is already part of its implementation. For members (fields, methods, nested classes, and nested interfaces), there are four possible access levels, listed here in order of increasing accessibility: • private—The member is accessible only from the top-level class where it is declared. • package-private—The member is accessible from any class in the package where it is declared. Technically known as default access, this is the access lev- el you get if no access modifier is specified. • protected—The member is accessible from subclasses of the class where it is declared (subject to a few restrictions [JLS, 6.6.2]) and from any class in the package where it is declared. • public—The member is accessible from anywhere. www.it-ebooks.info ITEM 13: MINIMIZE THE ACCESSIBILITY OF CLASSES AND MEMBERS 69 After carefully designing your class’s public API, your reflex should be to make all other members private. Only if another class in the same package really needs to access a member should you remove the private modifier, making the member package-private. If you find yourself doing this often, you should reex- amine the design of your system to see if another decomposition might yield classes that are better decoupled from one another. That said, both private and package-private members are part of a class’s implementation and do not normally impact its exported API. These fields can, however, “leak” into the exported API if the class implements Serializable (Item 74, Item 75). For members of public classes, a huge increase in accessibility occurs when the access level goes from package-private to protected. A protected member is part of the class’s exported API and must be supported forever. Also, a protected member of an exported class represents a public commitment to an implementa- tion detail (Item 17). The need for protected members should be relatively rare. There is one rule that restricts your ability to reduce the accessibility of meth- ods. If a method overrides a superclass method, it is not permitted to have a lower access level in the subclass than it does in the superclass [JLS,]. This is necessary to ensure that an instance of the subclass is usable anywhere that an instance of the superclass is usable. If you violate this rule, the compiler will gen- erate an error message when you try to compile the subclass. A special case of this rule is that if a class implements an interface, all of the class methods that are also present in the interface must be declared public. This is so because all members of an interface are implicitly public [JLS, 9.1.5]. To facilitate testing, you may be tempted to make a class, interface, or mem- ber more accessible. This is fine up to a point. It is acceptable to make a private member of a public class package-private in order to test it, but it is not acceptable to raise the accessibility any higher than that. In other words, it is not acceptable to make a class, interface, or member a part of a package’s exported API to facilitate testing. Luckily, it isn’t necessary either, as tests can be made to run as part of the package being tested, thus gaining access to its package-private elements. Instance fields should never be public (Item 14). If an instance field is non- final, or is a final reference to a mutable object, then by making the field public, you give up the ability to limit the values that can be stored in the field. This means you also give up the ability to enforce invariants involving the field. Also, you give up the ability to take any action when the field is modified, so classes with public mutable fields are not thread-safe. Even if a field is final and refers to an immutable object, by making the field public you give up the flexibility to switch to a new internal data representation in which the field does not exist. www.it-ebooks.info CHAPTER 4 CLASSES AND INTERFACES70 The same advice applies to static fields, with the one exception. You can expose constants via public static final fields, assuming the constants form an inte- gral part of the abstraction provided by the class. By convention, such fields have names consisting of capital letters, with words separated by underscores (Item 56). It is critical that these fields contain either primitive values or references to immutable objects (Item 15). A final field containing a reference to a mutable object has all the disadvantages of a nonfinal field. While the reference cannot be modified, the referenced object can be modified—with disastrous results. Note that a nonzero-length array is always mutable, so it is wrong for a class to have a public static final array field, or an accessor that returns such a field. If a class has such a field or accessor, clients will be able to modify the con- tents of the array. This is a frequent source of security holes: // Potential security hole! public static final Thing[] VALUES = { ... }; Beware of the fact that many IDEs generate accessors that return references to pri- vate array fields, resulting in exactly this problem. There are two ways to fix the problem. You can make the public array private and add a public immutable list: private static final Thing[] PRIVATE_VALUES = { ... }; public static final List VALUES = Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES)); Alternatively, you can make the array private and add a public method that returns a copy of a private array: private static final Thing[] PRIVATE_VALUES = { ... }; public static final Thing[] values() { return PRIVATE_VALUES.clone(); } To choose between these alternatives, think about what the client is likely to do with the result. Which return type will be more convenient? Which will give bet- ter performance? To summarize, you should always reduce accessibility as much as possible. After carefully designing a minimal public API, you should prevent any stray classes, interfaces, or members from becoming a part of the API. With the excep- tion of public static final fields, public classes should have no public fields. Ensure that objects referenced by public static final fields are immutable. www.it-ebooks.info ITEM 14: IN PUBLIC CLASSES, USE ACCESSOR METHODS, NOT PUBLIC FIELDS 71 Item 14: In public classes, use accessor methods, not public fields Occasionally, you may be tempted to write degenerate classes that serve no pur- pose other than to group instance fields: // Degenerate classes like this should not be public! class Point { public double x; public double y; } Because the data fields of such classes are accessed directly, these classes do not offer the benefits of encapsulation (Item 13). You can’t change the representa- tion without changing the API, you can’t enforce invariants, and you can’t take auxiliary action when a field is accessed. Hard-line object-oriented programmers feel that such classes are anathema and should always be replaced by classes with private fields and public accessor methods (getters) and, for mutable classes, mutators (setters): // Encapsulation of data by accessor methods and mutators class Point { private double x; private double y; public Point(double x, double y) { this.x = x; this.y = y; } public double getX() { return x; } public double getY() { return y; } public void setX(double x) { this.x = x; } public void setY(double y) { this.y = y; } } Certainly, the hard-liners are correct when it comes to public classes: if a class is accessible outside its package, provide accessor methods, to preserve the flexibility to change the class’s internal representation. If a public class exposes its data fields, all hope of changing its representation is lost, as client code can be dis- tributed far and wide. However, if a class is package-private or is a private nested class, there is nothing inherently wrong with exposing its data fields—assuming they do an www.it-ebooks.info CHAPTER 4 CLASSES AND INTERFACES72 adequate job of describing the abstraction provided by the class. This approach generates less visual clutter than the accessor-method approach, both in the class definition and in the client code that uses it. While the client code is tied to the class’s internal representation, this code is confined to the package containing the class. If a change in representation becomes desirable, you can make the change without touching any code outside the package. In the case of a private nested class, the scope of the change is further restricted to the enclosing class. Several classes in the Java platform libraries violate the advice that public classes should not expose fields directly. Prominent examples include the Point and Dimension classes in the java.awt package. Rather than examples to be emu- lated, these classes should be regarded as cautionary tales. As described in Item 55, the decision to expose the internals of the Dimension class resulted in a seri- ous performance problem that is still with us today. While it’s never a good idea for a public class to expose fields directly, it is less harmful if the fields are immutable. You can’t change the representation of such a class without changing its API, and you can’t take auxiliary actions when a field is read, but you can enforce invariants. For example, this class guarantees that each instance represents a valid time: // Public class with exposed immutable fields - questionable public final class Time { private static final int HOURS_PER_DAY = 24; private static final int MINUTES_PER_HOUR = 60; public final int hour; public final int minute; public Time(int hour, int minute) { if (hour < 0 || hour >= HOURS_PER_DAY) throw new IllegalArgumentException("Hour: " + hour); if (minute < 0 || minute >= MINUTES_PER_HOUR) throw new IllegalArgumentException("Min: " + minute); this.hour = hour; this.minute = minute; } ... // Remainder omitted } In summary, public classes should never expose mutable fields. It is less harmful, though still questionable, for public classes to expose immutable fields. It is, however, sometimes desirable for package-private or private nested classes to expose fields, whether mutable or immutable. www.it-ebooks.info ITEM 15: MINIMIZE MUTABILITY 73 Item 15: Minimize mutability An immutable class is simply a class whose instances cannot be modified. All of the information contained in each instance is provided when it is created and is fixed for the lifetime of the object. The Java platform libraries contain many immutable classes, including String, the boxed primitive classes, and BigInte- ger and BigDecimal. There are many good reasons for this: Immutable classes are easier to design, implement, and use than mutable classes. They are less prone to error and are more secure. To make a class immutable, follow these five rules: 1. Don’t provide any methods that modify the object’s state (known as muta- tors). 2. Ensure that the class can’t be extended. This prevents careless or malicious subclasses from compromising the immutable behavior of the class by behav- ing as if the object’s state has changed. Preventing subclassing is generally ac- complished by making the class final, but there is an alternative that we’ll discuss later. 3. Make all fields final. This clearly expresses your intent in a manner that is en- forced by the system. Also, it is necessary to ensure correct behavior if a refer- ence to a newly created instance is passed from one thread to another without synchronization, as spelled out in the memory model [JLS, 17.5; Goetz06 16]. 4. Make all fields private. This prevents clients from obtaining access to muta- ble objects referred to by fields and modifying these objects directly. While it is technically permissible for immutable classes to have public final fields con- taining primitive values or references to immutable objects, it is not recom- mended because it precludes changing the internal representation in a later release (Item 13). 5. Ensure exclusive access to any mutable components. If your class has any fields that refer to mutable objects, ensure that clients of the class cannot obtain references to these objects. Never initialize such a field to a client-provided ob- ject reference or return the object reference from an accessor. Make defensive copies (Item 39) in constructors, accessors, and readObject methods (Item 76). www.it-ebooks.info CHAPTER 4 CLASSES AND INTERFACES74 Many of the example classes in previous items are immutable. One such class is PhoneNumber in Item 9, which has accessors for each attribute but no corre- sponding mutators. Here is a slightly more complex example: public final class Complex { private final double re; private final double im; public Complex(double re, double im) { this.re = re; this.im = im; } // Accessors with no corresponding mutators public double realPart() { return re; } public double imaginaryPart() { return im; } public Complex add(Complex c) { return new Complex(re + c.re, im + c.im); } public Complex subtract(Complex c) { return new Complex(re - c.re, im - c.im); } public Complex multiply(Complex c) { return new Complex(re * c.re - im * c.im, re * c.im + im * c.re); } public Complex divide(Complex c) { double tmp = c.re * c.re + c.im * c.im; return new Complex((re * c.re + im * c.im) / tmp, (im * c.re - re * c.im) / tmp); } @Override public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof Complex)) return false; Complex c = (Complex) o; // See page 43 to find out why we use compare instead of == return Double.compare(re, c.re) == 0 && Double.compare(im, c.im) == 0; } www.it-ebooks.info ITEM 15: MINIMIZE MUTABILITY 75 @Override public int hashCode() { int result = 17 + hashDouble(re); result = 31 * result + hashDouble(im); return result; } private int hashDouble(double val) { long longBits = Double.doubleToLongBits(re); return (int) (longBits ^ (longBits >>> 32)); } @Override public String toString() { return "(" + re + " + " + im + "i)"; } } This class represents a complex number (a number with both real and imagi- nary parts). In addition to the standard Object methods, it provides accessors for the real and imaginary parts and provides the four basic arithmetic operations: addition, subtraction, multiplication, and division. Notice how the arithmetic operations create and return a new Complex instance rather than modifying this instance. This pattern is used in most nontrivial immutable classes. It is known as the functional approach because methods return the result of applying a function to their operand without modifying it. Contrast this to the more common proce- dural or imperative approach in which methods apply a procedure to their oper- and, causing its state to change. The functional approach may appear unnatural if you’re not familiar with it, but it enables immutability, which has many advantages. Immutable objects are simple. An immutable object can be in exactly one state, the state in which it was created. If you make sure that all constructors establish class invariants, then it is guaranteed that these invariants will remain true for all time, with no further effort on your part or on the part of the programmer who uses the class. Mutable objects, on the other hand, can have arbitrarily complex state spaces. If the documentation does not provide a precise description of the state transitions performed by muta- tor methods, it can be difficult or impossible to use a mutable class reliably. Immutable objects are inherently thread-safe; they require no synchroni- zation. They cannot be corrupted by multiple threads accessing them concur- rently. This is far and away the easiest approach to achieving thread safety. In fact, no thread can ever observe any effect of another thread on an immutable object. Therefore, immutable objects can be shared freely. Immutable classes should take advantage of this by encouraging clients to reuse existing instances wherever www.it-ebooks.info CHAPTER 4 CLASSES AND INTERFACES76 possible. One easy way to do this is to provide public static final constants for fre- quently used values. For example, the Complex class might provide these con- stants: public static final Complex ZERO = new Complex(0, 0); public static final Complex ONE = new Complex(1, 0); public static final Complex I = new Complex(0, 1); This approach can be taken one step further. An immutable class can provide static factories (Item 1) that cache frequently requested instances to avoid creating new instances when existing ones would do. All the boxed primitive classes and BigInteger do this. Using such static factories causes clients to share instances instead of creating new ones, reducing memory footprint and garbage collection costs. Opting for static factories in place of public constructors when designing a new class gives you the flexibility to add caching later, without modifying clients. A consequence of the fact that immutable objects can be shared freely is that you never have to make defensive copies (Item 39). In fact, you never have to make any copies at all because the copies would be forever equivalent to the orig- inals. Therefore, you need not and should not provide a clone method or copy constructor (Item 11) on an immutable class. This was not well understood in the early days of the Java platform, so the String class does have a copy constructor, but it should rarely, if ever, be used (Item 5). Not only can you share immutable objects, but you can share their inter- nals. For example, the BigInteger class uses a sign-magnitude representation internally. The sign is represented by an int, and the magnitude is represented by an int array. The negate method produces a new BigInteger of like magnitude and opposite sign. It does not need to copy the array; the newly created BigInte- ger points to the same internal array as the original. Immutable objects make great building blocks for other objects, whether mutable or immutable. It’s much easier to maintain the invariants of a complex object if you know that its component objects will not change underneath it. A special case of this principle is that immutable objects make great map keys and set elements: you don’t have to worry about their values changing once they’re in the map or set, which would destroy the map or set’s invariants. The only real disadvantage of immutable classes is that they require a separate object for each distinct value. Creating these objects can be costly, especially if they are large. For example, suppose that you have a million-bit Big- Integer and you want to change its low-order bit: www.it-ebooks.info ITEM 15: MINIMIZE MUTABILITY 77 BigInteger moby = ...; moby = moby.flipBit(0); The flipBit method creates a new BigInteger instance, also a million bits long, that differs from the original in only one bit. The operation requires time and space proportional to the size of the BigInteger. Contrast this to java.util.BitSet. Like BigInteger, BitSet represents an arbitrarily long sequence of bits, but unlike BigInteger, BitSet is mutable. The BitSet class provides a method that allows you to change the state of a single bit of a million- bit instance in constant time. The performance problem is magnified if you perform a multistep operation that generates a new object at every step, eventually discarding all objects except the final result. There are two approaches to coping with this problem. The first is to guess which multistep operations will be commonly required and provide them as primitives. If a multistep operation is provided as a primitive, the immutable class does not have to create a separate object at each step. Internally, the immuta- ble class can be arbitrarily clever. For example, BigInteger has a package-private mutable “companion class” that it uses to speed up multistep operations such as modular exponentiation. It is much harder to use the mutable companion class than to use BigInteger for all of the reasons outlined earlier, but luckily you don’t have to: the implementors of BigInteger did the hard work for you. The package-private mutable companion class approach works fine if you can accurately predict which complex multistage operations clients will want to perform on your immutable class. If not, then your best bet is to provide a public mutable companion class. The main example of this approach in the Java platform libraries is the String class, whose mutable companion is StringBuilder (and the largely obsolete StringBuffer). Arguably, BitSet plays the role of mutable companion to BigInteger under certain circumstances. Now that you know how to make an immutable class and you understand the pros and cons of immutability, let’s discuss a few design alternatives. Recall that to guarantee immutability, a class must not permit itself to be subclassed. Typically this is done by making the class final, but there is another, more flexible way to do it. The alternative to making an immutable class final is to make all of its constructors private or package-private, and to add public static factories in place of the public constructors (Item 1). www.it-ebooks.info CHAPTER 4 CLASSES AND INTERFACES78 To make this concrete, here’s how Complex would look if you took this approach: // Immutable class with static factories instead of constructors public class Complex { private final double re; private final double im; private Complex(double re, double im) { this.re = re; this.im = im; } public static Complex valueOf(double re, double im) { return new Complex(re, im); } ... // Remainder unchanged } While this approach is not commonly used, it is often the best alternative. It is the most flexible because it allows the use of multiple package-private implemen- tation classes. To its clients that reside outside its package, the immutable class is effectively final because it is impossible to extend a class that comes from another package and that lacks a public or protected constructor. Besides allowing the flexibility of multiple implementation classes, this approach makes it possible to tune the performance of the class in subsequent releases by improving the object- caching capabilities of the static factories. Static factories have many other advantages over constructors, as discussed in Item 1. For example, suppose that you want to provide a means of creating a com- plex number based on its polar coordinates. This would be very messy using con- structors because the natural constructor would have the same signature that we already used: Complex(double, double). With static factories it’s easy. Just add a second static factory with a name that clearly identifies its function: public static Complex valueOfPolar(double r, double theta) { return new Complex(r * Math.cos(theta), r * Math.sin(theta)); } It was not widely understood that immutable classes had to be effectively final when BigInteger and BigDecimal were written, so all of their methods may be www.it-ebooks.info ITEM 15: MINIMIZE MUTABILITY 79 overridden. Unfortunately, this could not be corrected after the fact while preserv- ing backward compatibility. If you write a class whose security depends on the immutability of a BigInteger or BigDecimal argument from an untrusted client, you must check to see that the argument is a “real” BigInteger or BigDecimal, rather than an instance of an untrusted subclass. If it is the latter, you must defen- sively copy it under the assumption that it might be mutable (Item 39): public static BigInteger safeInstance(BigInteger val) { if (val.getClass() != BigInteger.class) return new BigInteger(val.toByteArray()); return val; } The list of rules for immutable classes at the beginning of this item says that no methods may modify the object and that all its fields must be final. In fact these rules are a bit stronger than necessary and can be relaxed to improve performance. In truth, no method may produce an externally visible change in the object’s state. However, some immutable classes have one or more nonfinal fields in which they cache the results of expensive computations the first time they are needed. If the same value is requested again, the cached value is returned, saving the cost of recalculation. This trick works precisely because the object is immutable, which guarantees that the computation would yield the same result if it were repeated. For example, PhoneNumber’s hashCode method (Item 9, page 49) computes the hash code the first time it’s invoked and caches it in case it’s invoked again. This technique, an example of lazy initialization (Item 71), is also used by String. One caveat should be added concerning serializability. If you choose to have your immutable class implement Serializable and it contains one or more fields that refer to mutable objects, you must provide an explicit readObject or readResolve method, or use the ObjectOutputStream.writeUnshared and ObjectInputStream.readUnshared methods, even if the default serialized form is acceptable. Otherwise an attacker could create a mutable instance of your not- quite-immutable class. This topic is covered in detail in Item 76. To summarize, resist the urge to write a set method for every get method. Classes should be immutable unless there’s a very good reason to make them mutable. Immutable classes provide many advantages, and their only disadvan- tage is the potential for performance problems under certain circumstances. You should always make small value objects, such as PhoneNumber and Complex, immutable. (There are several classes in the Java platform libraries, such as www.it-ebooks.info CHAPTER 4 CLASSES AND INTERFACES80 java.util.Date and java.awt.Point, that should have been immutable but aren’t.) You should seriously consider making larger value objects, such as String and BigInteger, immutable as well. You should provide a public mutable companion class for your immutable class only once you’ve confirmed that it’s necessary to achieve satisfactory performance (Item 55). There are some classes for which immutability is impractical. If a class can- not be made immutable, limit its mutability as much as possible. Reducing the number of states in which an object can exist makes it easier to reason about the object and reduces the likelihood of errors. Therefore, make every field final unless there is a compelling reason to make it nonfinal. Constructors should create fully initialized objects with all of their invariants established. Don’t provide a public initialization method separate from the con- structor or static factory unless there is a compelling reason to do so. Similarly, don’t provide a “reinitialize” method that enables an object to be reused as if it had been constructed with a different initial state. Such methods generally provide little if any performance benefit at the expense of increased complexity. The TimerTask class exemplifies these principles. It is mutable, but its state space is kept intentionally small. You create an instance, schedule it for execution, and optionally cancel it. Once a timer task has run to completion or has been can- celed, you may not reschedule it. A final note should be added concerning the Complex class in this item. This example was meant only to illustrate immutability. It is not an industrial-strength complex number implementation. It uses the standard formulas for complex multiplication and division, which are not correctly rounded and provide poor semantics for complex NaNs and infinities [Kahan91, Smith62, Thomas94]. www.it-ebooks.info ITEM 16: FAVOR COMPOSITION OVER INHERITANCE 81 Item 16: Favor composition over inheritance Inheritance is a powerful way to achieve code reuse, but it is not always the best tool for the job. Used inappropriately, it leads to fragile software. It is safe to use inheritance within a package, where the subclass and the superclass implementa- tions are under the control of the same programmers. It is also safe to use inherit- ance when extending classes specifically designed and documented for extension (Item 17). Inheriting from ordinary concrete classes across package boundaries, however, is dangerous. As a reminder, this book uses the word “inheritance” to mean implementation inheritance (when one class extends another). The problems discussed in this item do not apply to interface inheritance (when a class imple- ments an interface or where one interface extends another). Unlike method invocation, inheritance violates encapsulation [Snyder86]. In other words, a subclass depends on the implementation details of its superclass for its proper function. The superclass’s implementation may change from release to release, and if it does, the subclass may break, even though its code has not been touched. As a consequence, a subclass must evolve in tandem with its super- class, unless the superclass’s authors have designed and documented it specifi- cally for the purpose of being extended. To make this concrete, let’s suppose we have a program that uses a HashSet. To tune the performance of our program, we need to query the HashSet as to how many elements have been added since it was created (not to be confused with its current size, which goes down when an element is removed). To provide this func- tionality, we write a HashSet variant that keeps count of the number of attempted element insertions and exports an accessor for this count. The HashSet class con- tains two methods capable of adding elements, add and addAll, so we override both of these methods: // Broken - Inappropriate use of inheritance! public class InstrumentedHashSet extends HashSet { // The number of attempted element insertions private int addCount = 0; public InstrumentedHashSet() { } public InstrumentedHashSet(int initCap, float loadFactor) { super(initCap, loadFactor); } www.it-ebooks.info CHAPTER 4 CLASSES AND INTERFACES82 @Override public boolean add(E e) { addCount++; return super.add(e); } @Override public boolean addAll(Collection c) { addCount += c.size(); return super.addAll(c); } public int getAddCount() { return addCount; } } This class looks reasonable, but it doesn’t work. Suppose we create an instance and add three elements using the addAll method: InstrumentedHashSet s = new InstrumentedHashSet(); s.addAll(Arrays.asList("Snap", "Crackle", "Pop")); We would expect the getAddCount method to return three at this point, but it returns six. What went wrong? Internally, HashSet’s addAll method is imple- mented on top of its add method, although HashSet, quite reasonably, does not document this implementation detail. The addAll method in InstrumentedHash- Set added three to addCount and then invoked HashSet’s addAll implementation using super.addAll. This in turn invoked the add method, as overridden in InstrumentedHashSet, once for each element. Each of these three invocations added one more to addCount, for a total increase of six: each element added with the addAll method is double-counted. We could “fix” the subclass by eliminating its override of the addAll method. While the resulting class would work, it would depend for its proper function on the fact that HashSet’s addAll method is implemented on top of its add method. This “self-use” is an implementation detail, not guaranteed to hold in all imple- mentations of the Java platform and subject to change from release to release. Therefore, the resulting InstrumentedHashSet class would be fragile. It would be slightly better to override the addAll method to iterate over the specified collection, calling the add method once for each element. This would guarantee the correct result whether or not HashSet’s addAll method were implemented atop its add method, because HashSet’s addAll implementation would no longer be invoked. This technique, however, does not solve all our problems. It amounts to reimplementing superclass methods that may or may not www.it-ebooks.info ITEM 16: FAVOR COMPOSITION OVER INHERITANCE 83 result in self-use, which is difficult, time-consuming, and error-prone. Additionally, it isn’t always possible, as some methods cannot be implemented without access to private fields inaccessible to the subclass. A related cause of fragility in subclasses is that their superclass can acquire new methods in subsequent releases. Suppose a program depends for its security on the fact that all elements inserted into some collection satisfy some predicate. This can be guaranteed by subclassing the collection and overriding each method capable of adding an element to ensure that the predicate is satisfied before adding the element. This works fine until a new method capable of inserting an element is added to the superclass in a subsequent release. Once this happens, it becomes possible to add an “illegal” element merely by invoking the new method, which is not overridden in the subclass. This is not a purely theoretical problem. Several security holes of this nature had to be fixed when Hashtable and Vector were ret- rofitted to participate in the Collections Framework. Both of the above problems stem from overriding methods. You might think that it is safe to extend a class if you merely add new methods and refrain from overriding existing methods. While this sort of extension is much safer, it is not without risk. If the superclass acquires a new method in a subsequent release and you have the bad luck to have given the subclass a method with the same signature and a different return type, your subclass will no longer compile [JLS,]. If you’ve given the subclass a method with the same signature and return type as the new superclass method, then you’re now overriding it, so you’re subject to the two problems described above. Furthermore, it is doubtful that your method will fulfill the contract of the new superclass method, as that contract had not yet been writ- ten when you wrote the subclass method. Luckily, there is a way to avoid all of the problems described earlier. Instead of extending an existing class, give your new class a private field that references an instance of the existing class. This design is called composition because the existing class becomes a component of the new one. Each instance method in the new class invokes the corresponding method on the contained instance of the existing class and returns the results. This is known as forwarding, and the meth- ods in the new class are known as forwarding methods. The resulting class will be rock solid, with no dependencies on the implementation details of the existing class. Even adding new methods to the existing class will have no impact on the new class. To make this concrete, here’s a replacement for InstrumentedHashSet that uses the composition-and-forwarding approach. Note that the implementation is broken into two pieces, the class itself and a reusable forwarding class, which contains all of the forwarding methods and nothing else: www.it-ebooks.info CHAPTER 4 CLASSES AND INTERFACES84 // Wrapper class - uses composition in place of inheritance public class InstrumentedSet extends ForwardingSet { private int addCount = 0; public InstrumentedSet(Set s) { super(s); } @Override public boolean add(E e) { addCount++; return super.add(e); } @Override public boolean addAll(Collection c) { addCount += c.size(); return super.addAll(c); } public int getAddCount() { return addCount; } } // Reusable forwarding class public class ForwardingSet implements Set { private final Set s; public ForwardingSet(Set s) { this.s = s; } public void clear() { s.clear(); } public boolean contains(Object o) { return s.contains(o); } public boolean isEmpty() { return s.isEmpty(); } public int size() { return s.size(); } public Iterator iterator() { return s.iterator(); } public boolean add(E e) { return s.add(e); } public boolean remove(Object o) { return s.remove(o); } public boolean containsAll(Collection c) { return s.containsAll(c); } public boolean addAll(Collection c) { return s.addAll(c); } public boolean removeAll(Collection c) { return s.removeAll(c); } public boolean retainAll(Collection c) { return s.retainAll(c); } public Object[] toArray() { return s.toArray(); } public T[] toArray(T[] a) { return s.toArray(a); } @Override public boolean equals(Object o) { return s.equals(o); } @Override public int hashCode() { return s.hashCode(); } @Override public String toString() { return s.toString(); } } www.it-ebooks.info ITEM 16: FAVOR COMPOSITION OVER INHERITANCE 85 The design of the InstrumentedSet class is enabled by the existence of the Set interface, which captures the functionality of the HashSet class. Besides being robust, this design is extremely flexible. The InstrumentedSet class imple- ments the Set interface and has a single constructor whose argument is also of type Set. In essence, the class transforms one Set into another, adding the instru- mentation functionality. Unlike the inheritance-based approach, which works only for a single concrete class and requires a separate constructor for each supported constructor in the superclass, the wrapper class can be used to instrument any Set implementation and will work in conjunction with any preexisting constructor: Set s = new InstrumentedSet(new TreeSet(cmp)); Set s2 = new InstrumentedSet(new HashSet(capacity)); The InstrumentedSet class can even be used to temporarily instrument a set instance that has already been used without instrumentation: static void walk(Set dogs) { InstrumentedSet iDogs = new InstrumentedSet(dogs); ... // Within this method use iDogs instead of dogs } The InstrumentedSet class is known as a wrapper class because each InstrumentedSet instance contains (“wraps”) another Set instance. This is also known as the Decorator pattern [Gamma95, p. 175], because the Instrumented- Set class “decorates” a set by adding instrumentation. Sometimes the combina- tion of composition and forwarding is loosely referred to as delegation. Technically it’s not delegation unless the wrapper object passes itself to the wrapped object [Lieberman86; Gamma95, p. 20]. The disadvantages of wrapper classes are few. One caveat is that wrapper classes are not suited for use in callback frameworks, wherein objects pass self- references to other objects for subsequent invocations (“callbacks”). Because a wrapped object doesn’t know of its wrapper, it passes a reference to itself (this) and callbacks elude the wrapper. This is known as the SELF problem [Lieberman86]. Some people worry about the performance impact of forwarding method invocations or the memory footprint impact of wrapper objects. Neither turn out to have much impact in practice. It’s tedious to write forwarding methods, but you have to write the forwarding class for each interface only once, and for- warding classes may be provided for you by the package containing the interface. Inheritance is appropriate only in circumstances where the subclass really is a subtype of the superclass. In other words, a class B should extend a class A only if www.it-ebooks.info CHAPTER 4 CLASSES AND INTERFACES86 an “is-a” relationship exists between the two classes. If you are tempted to have a class B extend a class A, ask yourself the question: Is every B really an A? If you cannot truthfully answer yes to this question, B should not extend A. If the answer is no, it is often the case that B should contain a private instance of A and expose a smaller and simpler API: A is not an essential part of B, merely a detail of its implementation. There are a number of obvious violations of this principle in the Java platform libraries. For example, a stack is not a vector, so Stack should not extend Vector. Similarly, a property list is not a hash table, so Properties should not extend Hashtable. In both cases, composition would have been preferable. If you use inheritance where composition is appropriate, you needlessly expose implementation details. The resulting API ties you to the original imple- mentation, forever limiting the performance of your class. More seriously, by exposing the internals you let the client access them directly. At the very least, this can lead to confusing semantics. For example, if p refers to a Properties instance, then p.getProperty(key) may yield different results from p.get(key): the former method takes defaults into account, while the latter method, which is inherited from Hashtable, does not. Most seriously, the client may be able to corrupt invariants of the subclass by modifying the superclass directly. In the case of Properties, the designers intended that only strings be allowed as keys and values, but direct access to the underlying Hashtable allows this invariant to be violated. Once this invariant is violated, it is no longer possible to use other parts of the Properties API (load and store). By the time this prob- lem was discovered, it was too late to correct it because clients depended on the use of nonstring keys and values. There is one last set of questions you should ask yourself before deciding to use inheritance in place of composition. Does the class that you contemplate extending have any flaws in its API? If so, are you comfortable propagating those flaws into your class’s API? Inheritance propagates any flaws in the superclass’s API, while composition lets you design a new API that hides these flaws. To summarize, inheritance is powerful, but it is problematic because it violates encapsulation. It is appropriate only when a genuine subtype relationship exists between the subclass and the superclass. Even then, inheritance may lead to fragility if the subclass is in a different package from the superclass and the superclass is not designed for inheritance. To avoid this fragility, use composition and forwarding instead of inheritance, especially if an appropriate interface to implement a wrapper class exists. Not only are wrapper classes more robust than subclasses, they are also more powerful. www.it-ebooks.info ITEM 17: DESIGN AND DOCUMENT FOR INHERITANCE OR ELSE PROHIBIT IT 87 Item 17: Design and document for inheritance or else prohibit it Item 16 alerted you to the dangers of subclassing a “foreign” class that was not designed and documented for inheritance. So what does it mean for a class to be designed and documented for inheritance? First, the class must document precisely the effects of overriding any method. In other words, the class must document its self-use of overridable methods. For each public or protected method or constructor, the documentation must indicate which overridable methods the method or constructor invokes, in what sequence, and how the results of each invocation affect subsequent processing. (By overridable, we mean nonfinal and either public or protected.) More generally, a class must document any circumstances under which it might invoke an overridable method. For example, invocations might come from background threads or static initializers. By convention, a method that invokes overridable methods contains a descrip- tion of these invocations at the end of its documentation comment. The descrip- tion begins with the phrase “This implementation.” This phrase should not be taken to indicate that the behavior may change from release to release. It connotes that the description concerns the inner workings of the method. Here’s an exam- ple, copied from the specification for java.util.AbstractCollection: public boolean remove(Object o) Removes a single instance of the specified element from this collection, if it is present (optional operation). More formally, removes an element e such that (o==null ? e==null : o.equals(e)), if the collection contains one or more such elements. Returns true if the collection contained the specified element (or equivalently, if the collection changed as a result of the call). This implementation iterates over the collection looking for the specified el- ement. If it finds the element, it removes the element from the collection us- ing the iterator’s remove method. Note that this implementation throws an UnsupportedOperationException if the iterator returned by this collec- tion’s iterator method does not implement the remove method. This documentation leaves no doubt that overriding the iterator method will affect the behavior of the remove method. Furthermore, it describes exactly how the behavior of the Iterator returned by the iterator method will affect the behavior of the remove method. Contrast this to the situation in Item 16, where the www.it-ebooks.info CHAPTER 4 CLASSES AND INTERFACES88 programmer subclassing HashSet simply could not say whether overriding the add method would affect the behavior of the addAll method. But doesn’t this violate the dictum that good API documentation should describe what a given method does and not how it does it? Yes, it does! This is an unfortunate consequence of the fact that inheritance violates encapsulation. To document a class so that it can be safely subclassed, you must describe implemen- tation details that should otherwise be left unspecified. Design for inheritance involves more than just documenting patterns of self- use. To allow programmers to write efficient subclasses without undue pain, a class may have to provide hooks into its internal workings in the form of judi- ciously chosen protected methods or, in rare instances, protected fields. For example, consider the removeRange method from java.util.AbstractList: protected void removeRange(int fromIndex, int toIndex) Removes from this list all of the elements whose index is between fromIndex, inclusive, and toIndex, exclusive. Shifts any succeeding elements to the left (reduces their index). This call shortens the ArrayList by (toIndex - fromIndex) elements. (If toIndex == fromIndex, this operation has no effect.) This method is called by the clear operation on this list and its sublists. Overriding this method to take advantage of the internals of the list imple- mentation can substantially improve the performance of the clear operation on this list and its sublists. This implementation gets a list iterator positioned before fromIndex and re- peatedly calls ListIterator.next followed by ListIterator.remove, un- til the entire range has been removed. Note: If ListIterator.remove requires linear time, this implementation requires quadratic time. Parameters: fromIndex index of first element to be removed. toIndex index after last element to be removed. This method is of no interest to end users of a List implementation. It is provided solely to make it easy for subclasses to provide a fast clear method on sublists. In the absence of the removeRange method, subclasses would have to make do with quadratic performance when the clear method was invoked on sublists or rewrite the entire subList mechanism from scratch—not an easy task! www.it-ebooks.info ITEM 17: DESIGN AND DOCUMENT FOR INHERITANCE OR ELSE PROHIBIT IT 89 So how do you decide what protected members to expose when you design a class for inheritance? Unfortunately, there is no magic bullet. The best you can do is to think hard, take your best guess, and then test it by writing subclasses. You should expose as few protected members as possible, because each one represents a commitment to an implementation detail. On the other hand, you must not expose too few, as a missing protected member can render a class practically unusable for inheritance. The only way to test a class designed for inheritance is to write subclasses. If you omit a crucial protected member, trying to write a subclass will make the omission painfully obvious. Conversely, if several subclasses are written and none uses a protected member, you should probably make it private. Experience shows that three subclasses are usually sufficient to test an extendable class. One or more of these subclasses should be written by someone other than the superclass author. When you design for inheritance a class that is likely to achieve wide use, realize that you are committing forever to the self-use patterns that you document and to the implementation decisions implicit in its protected methods and fields. These commitments can make it difficult or impossible to improve the perfor- mance or functionality of the class in a subsequent release. Therefore, you must test your class by writing subclasses before you release it. Also, note that the special documentation required for inheritance clutters up normal documentation, which is designed for programmers who create instances of your class and invoke methods on them. As of this writing, there is little in the way of tools or commenting conventions to separate ordinary API documentation from information of interest only to programmers implementing subclasses. There are a few more restrictions that a class must obey to allow inheritance. Constructors must not invoke overridable methods, directly or indirectly. If you violate this rule, program failure will result. The superclass constructor runs before the subclass constructor, so the overriding method in the subclass will get invoked before the subclass constructor has run. If the overriding method depends on any initialization performed by the subclass constructor, the method will not behave as expected. To make this concrete, here’s a class that violates this rule: public class Super { // Broken - constructor invokes an overridable method public Super() { overrideMe(); } public void overrideMe() { } } www.it-ebooks.info CHAPTER 4 CLASSES AND INTERFACES90 Here’s a subclass that overrides the overrideMe, method which is erroneously invoked by Super’s sole constructor: public final class Sub extends Super { private final Date date; // Blank final, set by constructor Sub() { date = new Date(); } // Overriding method invoked by superclass constructor @Override public void overrideMe() { System.out.println(date); } public static void main(String[] args) { Sub sub = new Sub(); sub.overrideMe(); } } You might expect this program to print out the date twice, but it prints out null the first time, because the overrideMe method is invoked by the Super construc- tor before the Sub constructor has a chance to initialize the date field. Note that this program observes a final field in two different states! Note also that if over- rideMe had invoked any method on date, the invocation would have thrown a NullPointerException when the Super constructor invoked overrideMe. The only reason this program doesn’t throw a NullPointerException as it stands is that the println method has special provisions for dealing with a null argument. The Cloneable and Serializable interfaces present special difficulties when designing for inheritance. It is generally not a good idea for a class designed for inheritance to implement either of these interfaces, as they place a substantial burden on programmers who extend the class. There are, however, special actions that you can take to allow subclasses to implement these interfaces without man- dating that they do so. These actions are described in Item 11 and Item 74. If you do decide to implement Cloneable or Serializable in a class designed for inheritance, you should be aware that because the clone and readObject methods behave a lot like constructors, a similar restriction applies: neither clone nor readObject may invoke an overridable method, directly or indirectly. In the case of the readObject method, the overriding method will run before the subclass’s state has been deserialized. In the case of the clone method, the overriding method will run before the subclass’s clone method has a chance to www.it-ebooks.info ITEM 17: DESIGN AND DOCUMENT FOR INHERITANCE OR ELSE PROHIBIT IT 91 fix the clone’s state. In either case, a program failure is likely to follow. In the case of clone, the failure can damage the original object as well as the clone. This can happen, for example, if the overriding method assumes it is modifying the clone’s copy of the object’s deep structure, but the copy hasn’t been made yet. Finally, if you decide to implement Serializable in a class designed for inheritance and the class has a readResolve or writeReplace method, you must make the readResolve or writeReplace method protected rather than private. If these methods are private, they will be silently ignored by subclasses. This is one more case where an implementation detail becomes part of a class’s API to permit inheritance. By now it should be apparent that designing a class for inheritance places substantial limitations on the class. This is not a decision to be undertaken lightly. There are some situations where it is clearly the right thing to do, such as abstract classes, including skeletal implementations of interfaces (Item 18). There are other situations where it is clearly the wrong thing to do, such as immutable classes (Item 15). But what about ordinary concrete classes? Traditionally, they are neither final nor designed and documented for subclassing, but this state of affairs is danger- ous. Each time a change is made in such a class, there is a chance that client classes that extend the class will break. This is not just a theoretical problem. It is not uncommon to receive subclassing-related bug reports after modifying the internals of a nonfinal concrete class that was not designed and documented for inheritance. The best solution to this problem is to prohibit subclassing in classes that are not designed and documented to be safely subclassed. There are two ways to prohibit subclassing. The easier of the two is to declare the class final. The alternative is to make all the constructors private or package-private and to add public static factories in place of the constructors. This alternative, which pro- vides the flexibility to use subclasses internally, is discussed in Item 15. Either approach is acceptable. This advice may be somewhat controversial, as many programmers have grown accustomed to subclassing ordinary concrete classes to add facilities such as instrumentation, notification, and synchronization or to limit functionality. If a class implements some interface that captures its essence, such as Set, List, or Map, then you should feel no compunction about prohibiting subclassing. The wrapper class pattern, described in Item 16, provides a superior alternative to inheritance for augmenting the functionality. www.it-ebooks.info CHAPTER 4 CLASSES AND INTERFACES92 If a concrete class does not implement a standard interface, then you may inconvenience some programmers by prohibiting inheritance. If you feel that you must allow inheritance from such a class, one reasonable approach is to ensure that the class never invokes any of its overridable methods and to document this fact. In other words, eliminate the class’s self-use of overridable methods entirely. In doing so, you’ll create a class that is reasonably safe to subclass. Overriding a method will never affect the behavior of any other method. You can eliminate a class’s self-use of overridable methods mechanically, without changing its behavior. Move the body of each overridable method to a pri- vate “helper method” and have each overridable method invoke its private helper method. Then replace each self-use of an overridable method with a direct invoca- tion of the overridable method’s private helper method. www.it-ebooks.info ITEM 18: PREFER INTERFACES TO ABSTRACT CLASSES 93 Item 18: Prefer interfaces to abstract classes The Java programming language provides two mechanisms for defining a type that permits multiple implementations: interfaces and abstract classes. The most obvious difference between the two mechanisms is that abstract classes are per- mitted to contain implementations for some methods while interfaces are not. A more important difference is that to implement the type defined by an abstract class, a class must be a subclass of the abstract class. Any class that defines all of the required methods and obeys the general contract is permitted to implement an interface, regardless of where the class resides in the class hierarchy. Because Java permits only single inheritance, this restriction on abstract classes severely con- strains their use as type definitions. Existing classes can be easily retrofitted to implement a new interface. All you have to do is add the required methods if they don’t yet exist and add an implements clause to the class declaration. For example, many existing classes were retrofitted to implement the Comparable interface when it was introduced into the platform. Existing classes cannot, in general, be retrofitted to extend a new abstract class. If you want to have two classes extend the same abstract class, you have to place the abstract class high up in the type hierarchy where it subclasses an ancestor of both classes. Unfortunately, this causes great collateral damage to the type hierarchy, forcing all descendants of the common ancestor to extend the new abstract class whether or not it is appropriate for them to do so. Interfaces are ideal for defining mixins. Loosely speaking, a mixin is a type that a class can implement in addition to its “primary type” to declare that it pro- vides some optional behavior. For example, Comparable is a mixin interface that allows a class to declare that its instances are ordered with respect to other mutu- ally comparable objects. Such an interface is called a mixin because it allows the optional functionality to be “mixed in” to the type’s primary functionality. Abstract classes can’t be used to define mixins for the same reason that they can’t be retrofitted onto existing classes: a class cannot have more than one parent, and there is no reasonable place in the class hierarchy to insert a mixin. Interfaces allow the construction of nonhierarchical type frameworks. Type hierarchies are great for organizing some things, but other things don’t fall neatly into a rigid hierarchy. For example, suppose we have an interface represent- ing a singer and another representing a songwriter: public interface Singer { AudioClip sing(Song s); } www.it-ebooks.info CHAPTER 4 CLASSES AND INTERFACES94 public interface Songwriter { Song compose(boolean hit); } In real life, some singers are also songwriters. Because we used interfaces rather than abstract classes to define these types, it is perfectly permissible for a single class to implement both Singer and Songwriter. In fact, we can define a third interface that extends both Singer and Songwriter and adds new methods that are appropriate to the combination: public interface SingerSongwriter extends Singer, Songwriter { AudioClip strum(); void actSensitive(); } You don’t always need this level of flexibility, but when you do, interfaces are a lifesaver. The alternative is a bloated class hierarchy containing a separate class for every supported combination of attributes. If there are n attributes in the type system, there are 2n possible combinations that you might have to support. This is what’s known as a combinatorial explosion. Bloated class hierarchies can lead to bloated classes containing many methods that differ only in the type of their argu- ments, as there are no types in the class hierarchy to capture common behaviors. Interfaces enable safe, powerful functionality enhancements via the wrap- per class idiom, described in Item 16. If you use abstract classes to define types, you leave the programmer who wants to add functionality with no alternative but to use inheritance. The resulting classes are less powerful and more fragile than wrapper classes. While interfaces are not permitted to contain method implementations, using interfaces to define types does not prevent you from providing implementation assistance to programmers. You can combine the virtues of interfaces and abstract classes by providing an abstract skeletal implementation class to go with each nontrivial interface that you export. The interface still defines the type, but the skeletal implementation takes all of the work out of implementing it. By convention, skeletal implementations are called AbstractInterface, where Interface is the name of the interface they implement. For example, the Collec- tions Framework provides a skeletal implementation to go along with each main collection interface: AbstractCollection, AbstractSet, AbstractList, and AbstractMap. Arguably it would have made sense to call them SkeletalCollec- tion, SkeletalSet, SkeletalList, and SkeletalMap, but the Abstract conven- tion is now firmly established. www.it-ebooks.info ITEM 18: PREFER INTERFACES TO ABSTRACT CLASSES 95 When properly designed, skeletal implementations can make it very easy for programmers to provide their own implementations of your interfaces. For exam- ple, here’s a static factory method containing a complete, fully functional List implementation: // Concrete implementation built atop skeletal implementation static List intArrayAsList(final int[] a) { if (a == null) throw new NullPointerException(); return new AbstractList() { public Integer get(int i) { return a[i]; // Autoboxing (Item 5) } @Override public Integer set(int i, Integer val) { int oldVal = a[i]; a[i] = val; // Auto-unboxing return oldVal; // Autoboxing } public int size() { return a.length; } }; } When you consider all that a List implementation does for you, this example is an impressive demonstration of the power of skeletal implementations. Inciden- tally, the example is an Adapter [Gamma95, p. 139] that allows an int array to be viewed as a list of Integer instances. Because of all the translation back and forth between int values and Integer instances (boxing and unboxing), its perfor- mance is not terribly good. Note that a static factory is provided and that the class is an inaccessible anonymous class (Item 22) hidden inside the static factory. The beauty of skeletal implementations is that they provide the implementa- tion assistance of abstract classes without imposing the severe constraints that abstract classes impose when they serve as type definitions. For most implemen- tors of an interface, extending the skeletal implementation is the obvious choice, but it is strictly optional. If a preexisting class cannot be made to extend the skele- tal implementation, the class can always implement the interface manually. Fur- thermore, the skeletal implementation can still aid the implementor’s task. The class implementing the interface can forward invocations of interface methods to a contained instance of a private inner class that extends the skeletal implementa- www.it-ebooks.info CHAPTER 4 CLASSES AND INTERFACES96 tion. This technique, known as simulated multiple inheritance, is closely related to the wrapper class idiom discussed in Item 16. It provides most of the benefits of multiple inheritance, while avoiding the pitfalls. Writing a skeletal implementation is a relatively simple, if somewhat tedious, process. First you must study the interface and decide which methods are the primitives in terms of which the others can be implemented. These primitives will be the abstract methods in your skeletal implementation. Then you must provide concrete implementations of all the other methods in the interface. For example, here’s a skeletal implementation of the Map.Entry interface: // Skeletal Implementation public abstract class AbstractMapEntry implements Map.Entry { // Primitive operations public abstract K getKey(); public abstract V getValue(); // Entries in modifiable maps must override this method public V setValue(V value) { throw new UnsupportedOperationException(); } // Implements the general contract of Map.Entry.equals @Override public boolean equals(Object o) { if (o == this) return true; if (! (o instanceof Map.Entry)) return false; Map.Entry arg = (Map.Entry) o; return equals(getKey(), arg.getKey()) && equals(getValue(), arg.getValue()); } private static boolean equals(Object o1, Object o2) { return o1 == null ? o2 == null : o1.equals(o2); } // Implements the general contract of Map.Entry.hashCode @Override public int hashCode() { return hashCode(getKey()) ^ hashCode(getValue()); } private static int hashCode(Object obj) { return obj == null ? 0 : obj.hashCode(); } } www.it-ebooks.info ITEM 18: PREFER INTERFACES TO ABSTRACT CLASSES 97 Because skeletal implementations are designed for inheritance, you should follow all of the design and documentation guidelines in Item 17. For brevity’s sake, the documentation comments were omitted from the previous example, but good documentation is absolutely essential for skeletal implementations. A minor variant on the skeletal implementation is the simple implementation, exemplified by AbstractMap.SimpleEntry. A simple implementation is like a skeletal implementation in that it implements an interface and is designed for inheritance, but it differs in that it isn’t abstract: it is the simplest possible working implementation. You can use it as it stands or subclass it as circumstances warrant. Using abstract classes to define types that permit multiple implementations has one great advantage over using interfaces: It is far easier to evolve an abstract class than an interface. If, in a subsequent release, you want to add a new method to an abstract class, you can always add a concrete method containing a reasonable default implementation. All existing implementations of the abstract class will then provide the new method. This does not work for interfaces. It is, generally speaking, impossible to add a method to a public interface without breaking all existing classes that implement the interface. Classes that previously implemented the interface will be missing the new method and won’t compile anymore. You could limit the damage somewhat by adding the new method to the skeletal implementation at the same time as you add it to the inter- face, but this really wouldn’t solve the problem. Any implementation that didn’t inherit from the skeletal implementation would still be broken. Public interfaces, therefore, must be designed carefully. Once an interface is released and widely implemented, it is almost impossible to change. You really must get it right the first time. If an interface contains a minor flaw, it will irritate you and its users forever. If an interface is severely deficient, it can doom an API. The best thing to do when releasing a new interface is to have as many program- mers as possible implement the interface in as many ways as possible before the interface is frozen. This will allow you to discover flaws while you can still cor- rect them. To summarize, an interface is generally the best way to define a type that permits multiple implementations. An exception to this rule is the case where ease of evolution is deemed more important than flexibility and power. Under these circumstances, you should use an abstract class to define the type, but only if you understand and can accept the limitations. If you export a nontrivial interface, you should strongly consider providing a skeletal implementation to go with it. Finally, you should design all of your public interfaces with the utmost care and test them thoroughly by writing multiple implementations. www.it-ebooks.info CHAPTER 4 CLASSES AND INTERFACES98 Item 19: Use interfaces only to define types When a class implements an interface, the interface serves as a type that can be used to refer to instances of the class. That a class implements an interface should therefore say something about what a client can do with instances of the class. It is inappropriate to define an interface for any other purpose. One kind of interface that fails this test is the so-called constant interface. Such an interface contains no methods; it consists solely of static final fields, each exporting a constant. Classes using these constants implement the interface to avoid the need to qualify constant names with a class name. Here is an example: // Constant interface antipattern - do not use! public interface PhysicalConstants { // Avogadro's number (1/mol) static final double AVOGADROS_NUMBER = 6.02214199e23; // Boltzmann constant (J/K) static final double BOLTZMANN_CONSTANT = 1.3806503e-23; // Mass of the electron (kg) static final double ELECTRON_MASS = 9.10938188e-31; } The constant interface pattern is a poor use of interfaces. That a class uses some constants internally is an implementation detail. Implementing a constant interface causes this implementation detail to leak into the class’s exported API. It is of no consequence to the users of a class that the class implements a constant interface. In fact, it may even confuse them. Worse, it represents a commitment: if in a future release the class is modified so that it no longer needs to use the con- stants, it still must implement the interface to ensure binary compatibility. If a nonfinal class implements a constant interface, all of its subclasses will have their namespaces polluted by the constants in the interface. There are several constant interfaces in the Java platform libraries, such as java.io.ObjectStreamConstants. These interfaces should be regarded as anomalies and should not be emulated. If you want to export constants, there are several reasonable choices. If the constants are strongly tied to an existing class or interface, you should add them to the class or interface. For example, all of the boxed numerical primitive classes, such as Integer and Double, export MIN_VALUE and MAX_VALUE constants. If the constants are best viewed as members of an enumerated type, you should export www.it-ebooks.info ITEM 19: USE INTERFACES ONLY TO DEFINE TYPES 99 them with an enum type (Item 30). Otherwise, you should export the constants with a noninstantiable utility class (Item 4). Here is a utility class version of the PhysicalConstants example above: // Constant utility class package com.effectivejava.science; public class PhysicalConstants { private PhysicalConstants() { } // Prevents instantiation public static final double AVOGADROS_NUMBER = 6.02214199e23; public static final double BOLTZMANN_CONSTANT = 1.3806503e-23; public static final double ELECTRON_MASS = 9.10938188e-31; } Normally a utility class requires clients to qualify constant names with a class name, for example, PhysicalConstants.AVOGADROS_NUMBER. If you make heavy use of the constants exported by a utility class, you can avoid the need for qualify- ing the constants with the class name by making use of the static import facility, introduced in release 1.5: // Use of static import to avoid qualifying constants import static com.effectivejava.science.PhysicalConstants.*; public class Test { double atoms(double mols) { return AVOGADROS_NUMBER * mols; } ... // Many more uses of PhysicalConstants justify static import } In summary, interfaces should be used only to define types. They should not be used to export constants. www.it-ebooks.info CHAPTER 4 CLASSES AND INTERFACES100 Item 20: Prefer class hierarchies to tagged classes Occasionally you may run across a class whose instances come in two or more flavors and contain a tag field indicating the flavor of the instance. For example, consider this class, which is capable of representing a circle or a rectangle: // Tagged class - vastly inferior to a class hierarchy! class Figure { enum Shape { RECTANGLE, CIRCLE }; // Tag field - the shape of this figure final Shape shape; // These fields are used only if shape is RECTANGLE double length; double width; // This field is used only if shape is CIRCLE double radius; // Constructor for circle Figure(double radius) { shape = Shape.CIRCLE; this.radius = radius; } // Constructor for rectangle Figure(double length, double width) { shape = Shape.RECTANGLE; this.length = length; this.width = width; } double area() { switch(shape) { case RECTANGLE: return length * width; case CIRCLE: return Math.PI * (radius * radius); default: throw new AssertionError(); } } } www.it-ebooks.info ITEM 20: PREFER CLASS HIERARCHIES TO TAGGED CLASSES 101 Such tagged classes have numerous shortcomings. They are cluttered with boilerplate, including enum declarations, tag fields, and switch statements. Read- ability is further harmed because multiple implementations are jumbled together in a single class. Memory footprint is increased because instances are burdened with irrelevant fields belonging to other flavors. Fields can’t be made final unless constructors initialize irrelevant fields, resulting in more boilerplate. Constructors must set the tag field and initialize the right data fields with no help from the com- piler: if you initialize the wrong fields, the program will fail at runtime. You can’t add a flavor to a tagged class unless you can modify its source file. If you do add a flavor, you must remember to add a case to every switch statement, or the class will fail at runtime. Finally, the data type of an instance gives no clue as to its fla- vor. In short, tagged classes are verbose, error-prone, and inefficient. Luckily, object-oriented languages such as Java offer a far better alternative for defining a single data type capable of representing objects of multiple flavors: subtyping. A tagged class is just a pallid imitation of a class hierarchy. To transform a tagged class into a class hierarchy, first define an abstract class containing an abstract method for each method in the tagged class whose behavior depends on the tag value. In the Figure class, there is only one such method, which is area. This abstract class is the root of the class hierarchy. If there are any methods whose behavior does not depend on the value of the tag, put them in this class. Similarly, if there are any data fields used by all the flavors, put them in this class. There are no such flavor-independent methods or fields in the Figure class. Next, define a concrete subclass of the root class for each flavor of the original tagged class. In our example, there are two: circle and rectangle. Include in each subclass the data fields particular to its flavor. In our example, radius is particular to circle, and length and width are particular to rectangle. Also include in each subclass the appropriate implementation of each abstract method in the root class. Here is the class hierarchy corresponding to the original Figure class: // Class hierarchy replacement for a tagged class abstract class Figure { abstract double area(); } class Circle extends Figure { final double radius; Circle(double radius) { this.radius = radius; } double area() { return Math.PI * (radius * radius); } } www.it-ebooks.info CHAPTER 4 CLASSES AND INTERFACES102 class Rectangle extends Figure { final double length; final double width; Rectangle(double length, double width) { this.length = length; this.width = width; } double area() { return length * width; } } This class hierarchy corrects every shortcoming of tagged classes noted previ- ously. The code is simple and clear, containing none of the boilerplate found in the original. The implementation of each flavor is allotted its own class, and none of these classes are encumbered by irrelevant data fields. All fields are final. The compiler ensures that each class’s constructor initializes its data fields, and that each class has an implementation for every abstract method declared in the root class. This eliminates the possibility of a runtime failure due to a missing switch case. Multiple programmers can extend the hierarchy independently and interop- erably without access to the source for the root class. There is a separate data type associated with each flavor, allowing programmers to indicate the flavor of a vari- able and to restrict variables and input parameters to a particular flavor. Another advantage of class hierarchies is that they can be made to reflect nat- ural hierarchical relationships among types, allowing for increased flexibility and better compile-time type checking. Suppose the tagged class in the original exam- ple also allowed for squares. The class hierarchy could be made to reflect the fact that a square is a special kind of rectangle (assuming both are immutable): class Square extends Rectangle { Square(double side) { super(side, side); } } Note that the fields in the above hierarchy are accessed directly rather than by accessor methods. This was done for brevity and would be unacceptable if the hierarchy were public (Item 14). In summary, tagged classes are seldom appropriate. If you’re tempted to write a class with an explicit tag field, think about whether the tag could be eliminated and the class replaced by a hierarchy. When you encounter an existing class with a tag field, consider refactoring it into a hierarchy. www.it-ebooks.info ITEM 21: USE FUNCTION OBJECTS TO REPRESENT STRATEGIES 103 Item 21: Use function objects to represent strategies Some languages support function pointers, delegates, lambda expressions, or sim- ilar facilities that allow programs to store and transmit the ability to invoke a par- ticular function. Such facilities are typically used to allow the caller of a function to specialize its behavior by passing in a second function. For example, the qsort function in C’s standard library takes a pointer to a comparator function, which qsort uses to compare the elements to be sorted. The comparator function takes two parameters, each of which is a pointer to an element. It returns a negative inte- ger if the element indicated by the first parameter is less than the one indicated by the second, zero if the two elements are equal, and a positive integer if the element indicated by the first parameter is greater than the one indicated by the second. Different sort orders can be obtained by passing in different comparator functions. This is an example of the Strategy pattern [Gamma95, p. 315]; the comparator function represents a strategy for sorting elements. Java does not provide function pointers, but object references can be used to achieve a similar effect. Invoking a method on an object typically performs some operation on that object. However, it is possible to define an object whose meth- ods perform operations on other objects, passed explicitly to the methods. An instance of a class that exports exactly one such method is effectively a pointer to that method. Such instances are known as function objects. For example, consider the following class: class StringLengthComparator { public int compare(String s1, String s2) { return s1.length() - s2.length(); } } This class exports a single method that takes two strings and returns a negative integer if the first string is shorter than the second, zero if the two strings are of equal length, and a positive integer if the first string is longer. This method is a comparator that orders strings based on their length instead of the more typical lexicographic ordering. A reference to a StringLengthComparator object serves as a “function pointer” to this comparator, allowing it to be invoked on arbitrary pairs of strings. In other words, a StringLengthComparator instance is a con- crete strategy for string comparison. As is typical for concrete strategy classes, the StringLengthComparator class is stateless: it has no fields, hence all instances of the class are functionally www.it-ebooks.info CHAPTER 4 CLASSES AND INTERFACES104 equivalent. Thus it should be a singleton to save on unnecessary object creation costs (Item 3, Item 5): class StringLengthComparator { private StringLengthComparator() { } public static final StringLengthComparator INSTANCE = new StringLengthComparator(); public int compare(String s1, String s2) { return s1.length() - s2.length(); } } To pass a StringLengthComparator instance to a method, we need an appropriate type for the parameter. It would do no good to use StringLengthComparator because clients would be unable to pass any other comparison strategy. Instead, we need to define a Comparator interface and modify StringLengthComparator to implement this interface. In other words, we need to define a strategy interface to go with the concrete strategy class. Here it is: // Strategy interface public interface Comparator { public int compare(T t1, T t2); } This definition of the Comparator interface happens to come from the java.util package, but there’s nothing magic about it: you could just as well have written it yourself. The Comparator interface is generic (Item 26) so that it is applicable to comparators for objects other than strings. Its compare method takes two parameters of type T (its formal type parameter) rather than String. The StringLengthComparator class shown above can be made to implement Compar- ator merely by declaring it to do so: class StringLengthComparator implements Comparator { ... // class body is identical to the one shown above } Concrete strategy classes are often declared using anonymous classes (Item 22). The following statement sorts an array of strings according to length: Arrays.sort(stringArray, new Comparator() { public int compare(String s1, String s2) { return s1.length() - s2.length(); } }); www.it-ebooks.info ITEM 21: USE FUNCTION OBJECTS TO REPRESENT STRATEGIES 105 But note that using an anonymous class in this way will create a new instance each time the call is executed. If it is to be executed repeatedly, consider storing the function object in a private static final field and reusing it. Another advantage of doing this is that you can give the field a descriptive name for the function object. Because the strategy interface serves as a type for all of its concrete strategy instances, a concrete strategy class needn’t be made public to export a concrete strategy. Instead, a “host class” can export a public static field (or static factory method) whose type is the strategy interface, and the concrete strategy class can be a private nested class of the host. In the example that follows, a static member class is used in preference to an anonymous class to allow the concrete strategy class to implement a second interface, Serializable: // Exporting a concrete strategy class Host { private static class StrLenCmp implements Comparator, Serializable { public int compare(String s1, String s2) { return s1.length() - s2.length(); } } // Returned comparator is serializable public static final Comparator STRING_LENGTH_COMPARATOR = new StrLenCmp(); ... // Bulk of class omitted } The String class uses this pattern to export a case-independent string com- parator via its CASE_INSENSITIVE_ORDER field. To summarize, a primary use of function pointers is to implement the Strategy pattern. To implement this pattern in Java, declare an interface to represent the strategy, and a class that implements this interface for each concrete strategy. When a concrete strategy is used only once, it is typically declared and instanti- ated as an anonymous class. When a concrete strategy is designed for repeated use, it is generally implemented as a private static member class and exported in a public static final field whose type is the strategy interface. www.it-ebooks.info CHAPTER 4 CLASSES AND INTERFACES106 Item 22: Favor static member classes over nonstatic A nested class is a class defined within another class. A nested class should exist only to serve its enclosing class. If a nested class would be useful in some other context, then it should be a top-level class. There are four kinds of nested classes: static member classes, nonstatic member classes, anonymous classes, and local classes. All but the first kind are known as inner classes. This item tells you when to use which kind of nested class and why. A static member class is the simplest kind of nested class. It is best thought of as an ordinary class that happens to be declared inside another class and has access to all of the enclosing class’s members, even those declared private. A static member class is a static member of its enclosing class and obeys the same accessibility rules as other static members. If it is declared private, it is accessible only within the enclosing class, and so forth. One common use of a static member class is as a public helper class, useful only in conjunction with its outer class. For example, consider an enum describing the operations supported by a calculator (Item 30). The Operation enum should be a public static member class of the Calculator class. Clients of Calculator could then refer to operations using names like Calculator.Operation.PLUS and Calculator.Operation.MINUS. Syntactically, the only difference between static and nonstatic member classes is that static member classes have the modifier static in their declarations. Despite the syntactic similarity, these two kinds of nested classes are very differ- ent. Each instance of a nonstatic member class is implicitly associated with an enclosing instance of its containing class. Within instance methods of a nonstatic member class, you can invoke methods on the enclosing instance or obtain a refer- ence to the enclosing instance using the qualified this construct [JLS, 15.8.4]. If an instance of a nested class can exist in isolation from an instance of its enclosing class, then the nested class must be a static member class: it is impossible to create an instance of a nonstatic member class without an enclosing instance. The association between a nonstatic member class instance and its enclosing instance is established when the former is created; it cannot be modified thereafter. Normally, the association is established automatically by invoking a nonstatic member class constructor from within an instance method of the enclosing class. It is possible, although rare, to establish the association manually using the expression enclosingInstance.new MemberClass(args). As you would expect, the association takes up space in the nonstatic member class instance and adds time to its construction. www.it-ebooks.info ITEM 22: FAVOR STATIC MEMBER CLASSES OVER NONSTATIC 107 One common use of a nonstatic member class is to define an Adapter [Gamma95, p. 139] that allows an instance of the outer class to be viewed as an instance of some unrelated class. For example, implementations of the Map inter- face typically use nonstatic member classes to implement their collection views, which are returned by Map’s keySet, entrySet, and values methods. Similarly, implementations of the collection interfaces, such as Set and List, typically use nonstatic member classes to implement their iterators: // Typical use of a nonstatic member class public class MySet extends AbstractSet { ... // Bulk of the class omitted public Iterator iterator() { return new MyIterator(); } private class MyIterator implements Iterator { ... } } If you declare a member class that does not require access to an enclosing instance, always put the static modifier in its declaration, making it a static rather than a nonstatic member class. If you omit this modifier, each instance will have an extraneous reference to its enclosing instance. Storing this reference costs time and space, and can result in the enclosing instance being retained when it would otherwise be eligible for garbage collection (Item 6). And should you ever need to allocate an instance without an enclosing instance, you’ll be unable to do so, as nonstatic member class instances are required to have an enclosing instance. A common use of private static member classes is to represent components of the object represented by their enclosing class. For example, consider a Map instance, which associates keys with values. Many Map implementations have an internal Entry object for each key-value pair in the map. While each entry is asso- ciated with a map, the methods on an entry (getKey, getValue, and setValue) do not need access to the map. Therefore, it would be wasteful to use a nonstatic member class to represent entries: a private static member class is best. If you accidentally omit the static modifier in the entry declaration, the map will still work, but each entry will contain a superfluous reference to the map, which wastes space and time. It is doubly important to choose correctly between a static and a nonstatic member class if the class in question is a public or protected member of an www.it-ebooks.info CHAPTER 4 CLASSES AND INTERFACES108 exported class. In this case, the member class is an exported API element and can- not be changed from a nonstatic to a static member class in a subsequent release without violating binary compatibility. Anonymous classes are unlike anything else in the Java programming lan- guage. As you would expect, an anonymous class has no name. It is not a member of its enclosing class. Rather than being declared along with other members, it is simultaneously declared and instantiated at the point of use. Anonymous classes are permitted at any point in the code where an expression is legal. Anonymous classes have enclosing instances if and only if they occur in a nonstatic context. But even if they occur in a static context, they cannot have any static members. There are many limitations on the applicability of anonymous classes. You can’t instantiate them except at the point they’re declared. You can’t perform instanceof tests or do anything else that requires you to name the class. You can’t declare an anonymous class to implement multiple interfaces, or to extend a class and implement an interface at the same time. Clients of an anonymous class can’t invoke any members except those it inherits from its supertype. Because anonymous classes occur in the midst of expressions, they must be kept short— about ten lines or fewer—or readability will suffer. One common use of anonymous classes is to create function objects (Item 21) on the fly. For example, the sort method invocation on page 104 sorts an array of strings according to their length using an anonymous Comparator instance. Another common use of anonymous classes is to create process objects, such as Runnable, Thread, or TimerTask instances. A third common use is within static factory methods (see the intArrayAsList method in Item 18). Local classes are the least frequently used of the four kinds of nested classes. A local class can be declared anywhere a local variable can be declared and obeys the same scoping rules. Local classes have attributes in common with each of the other kinds of nested classes. Like member classes, they have names and can be used repeatedly. Like anonymous classes, they have enclosing instances only if they are defined in a nonstatic context, and they cannot contain static members. And like anonymous classes, they should be kept short so as not to harm readability. To recap, there are four different kinds of nested classes, and each has its place. If a nested class needs to be visible outside of a single method or is too long to fit comfortably inside a method, use a member class. If each instance of the member class needs a reference to its enclosing instance, make it nonstatic; other- wise, make it static. Assuming the class belongs inside a method, if you need to create instances from only one location and there is a preexisting type that charac- terizes the class, make it an anonymous class; otherwise, make it a local class. www.it-ebooks.info 109 CHAPTER 5 Generics IN release 1.5, generics were added to Java. Before generics, you had to cast every object you read from a collection. If someone accidentally inserted an object of the wrong type, casts could fail at runtime. With generics, you tell the compiler what types of objects are permitted in each collection. The compiler inserts casts for you automatically and tells you at compile time if you try to insert an object of the wrong type. This results in programs that are both safer and clearer, but these benefits come with complications. This chapter tells you how to maximize the benefits and minimize the complications. For a more detailed treatment of this material, see Langer’s tutorial [Langer08] or Naftalin and Wadler’s book [Naftalin07]. Item 23: Don’t use raw types in new code First, a few terms. A class or interface whose declaration has one or more type parameters is a generic class or interface [JLS, 8.1.2, 9.1.2]. For example, as of release 1.5, the List interface has a single type parameter, E, representing the ele- ment type of the list. Technically the name of the interface is now List (read “list of E”), but people often call it List for short. Generic classes and interfaces are collectively known as generic types. Each generic type defines a set of parameterized types, which consist of the class or interface name followed by an angle-bracketed list of actual type parame- ters corresponding to the generic type’s formal type parameters [JLS, 4.4, 4.5]. For example, List (read “list of string”) is a parameterized type repre- senting a list whose elements are of type String. (String is the actual type parameter corresponding to the formal type parameter E.) Finally, each generic type defines a raw type, which is the name of the generic type used without any accompanying actual type parameters [JLS, 4.8]. For exam- www.it-ebooks.info CHAPTER 5 GENERICS110 ple, the raw type corresponding to List is List. Raw types behave as if all of the generic type information were erased from the type declaration. For all practi- cal purposes, the raw type List behaves the same way as the interface type List did before generics were added to the platform. Before release 1.5, this would have been an exemplary collection declaration: // Now a raw collection type - don't do this! /** * My stamp collection. Contains only Stamp instances. */ private final Collection stamps = ... ; If you accidentally put a coin into your stamp collection, the erroneous insertion compiles and runs without error: // Erroneous insertion of coin into stamp collection stamps.add(new Coin( ... )); You don’t get an error until you retrieve the coin from the stamp collection: // Now a raw iterator type - don't do this! for (Iterator i = stamps.iterator(); i.hasNext(); ) { Stamp s = (Stamp) i.next(); // Throws ClassCastException ... // Do something with the stamp } As mentioned throughout this book, it pays to discover errors as soon as pos- sible after they are made, ideally at compile time. In this case, you don’t discover the error till runtime, long after it has happened, and in code that is far removed from the code containing the error. Once you see the ClassCastException, you have to search through the code base looking for the method invocation that put the coin into the stamp collection. The compiler can’t help you, because it can’t understand the comment that says, “Contains only Stamp instances.” With generics, you replace the comment with an improved type declaration for the collection that tells the compiler the information that was previously hid- den in the comment: // Parameterized collection type - typesafe private final Collection stamps = ... ; www.it-ebooks.info ITEM 23: DON’T USE RAW TYPES IN NEW CODE 111 From this declaration the compiler knows that stamps should contain only Stamp instances and guarantees this to be the case, assuming your entire code base is compiled with a compiler from release 1.5 or later and all the code compiles with- out emitting (or suppressing; see Item 24) any warnings. When stamps is declared with a parameterized type, the erroneous insertion generates a compile-time error message that tells you exactly what is wrong: Test.java:9: add(Stamp) in Collection cannot be applied to (Coin) stamps.add(new Coin()); ^ As an added benefit, you no longer have to cast manually when removing ele- ments from collections. The compiler inserts invisible casts for you and guaran- tees that they won’t fail (assuming, again, that all of your code was compiled with a generics-aware compiler and did not produce or suppress any warnings). This is true whether you use a for-each loop (Item 46): // for-each loop over a parameterized collection - typesafe for (Stamp s : stamps) { // No cast ... // Do something with the stamp } or a traditional for loop: // for loop with parameterized iterator declaration - typesafe for (Iterator i = stamps.iterator(); i.hasNext(); ) { Stamp s = i.next(); // No cast necessary ... // Do something with the stamp } While the prospect of accidentally inserting a coin into a stamp collection may appear far-fetched, the problem is real. For example, it is easy to imagine someone putting a java.util.Date instance into a collection that is supposed to contain only java.sql.Date instances. As noted above, it is still legal to use collection types and other generic types without supplying type parameters, but you should not do it. If you use raw types, you lose all the safety and expressiveness benefits of generics. Given that you shouldn’t use raw types, why did the language designers allow them? To pro- vide compatibility. The Java platform was about to enter its second decade when generics were introduced, and there was an enormous amount of Java code in www.it-ebooks.info CHAPTER 5 GENERICS112 existence that did not use generics. It was deemed critical that all of this code remain legal and interoperable with new code that does use generics. It had to be legal to pass instances of parameterized types to methods that were designed for use with ordinary types, and vice versa. This requirement, known as migration compatibility, drove the decision to support raw types. While you shouldn’t use raw types such as List in new code, it is fine to use types that are parameterized to allow insertion of arbitrary objects, such as List. Just what is the difference between the raw type List and the parameterized type List? Loosely speaking, the former has opted out of generic type checking, while the latter has explicitly told the compiler that it is capable of holding objects of any type. While you can pass a List to a parameter of type List, you can’t pass it to a parameter of type List. There are subtyping rules for generics, and List is a subtype of the raw type List, but not of the parameterized type List (Item 25). As a conse- quence, you lose type safety if you use a raw type like List, but not if you use a parameterized type like List. To make this concrete, consider the following program: // Uses raw type (List) - fails at runtime! public static void main(String[] args) { List strings = new ArrayList(); unsafeAdd(strings, new Integer(42)); String s = strings.get(0); // Compiler-generated cast } private static void unsafeAdd(List list, Object o) { list.add(o); } This program compiles, but because it uses the raw type List, you get a warning: Test.java:10: warning: unchecked call to add(E) in raw type List list.add(o); ^ And indeed, if you run the program, you get a ClassCastException when the program tries to cast the result of the invocation strings.get(0) to a String. This is a compiler-generated cast, so it’s normally guaranteed to succeed, but in this case we ignored a compiler warning and paid the price. www.it-ebooks.info ITEM 23: DON’T USE RAW TYPES IN NEW CODE 113 If you replace the raw type List with the parameterized type List in the unsafeAdd declaration and try to recompile the program, you’ll find that it no longer compiles. Here is the error message: Test.java:5: unsafeAdd(List,Object) cannot be applied to (List,Integer) unsafeAdd(strings, new Integer(42)); ^ You might be tempted to use a raw type for a collection whose element type is unknown and doesn’t matter. For example, suppose you want to write a method that takes two sets and returns the number of elements they have in common. Here’s how you might write such a method if you were new to generics: // Use of raw type for unknown element type - don't do this! static int numElementsInCommon(Set s1, Set s2) { int result = 0; for (Object o1 : s1) if (s2.contains(o1)) result++; return result; } This method works but it uses raw types, which are dangerous. Since release 1.5, Java has provided a safe alternative known as unbounded wildcard types. If you want to use a generic type but you don’t know or care what the actual type parameter is, you can use a question mark instead. For example, the unbounded wildcard type for the generic type Set is Set (read “set of some type”). It is the most general parameterized Set type, capable of holding any set. Here is how the numElementsInCommon method looks with unbounded wildcard types: // Unbounded wildcard type - typesafe and flexible static int numElementsInCommon(Set s1, Set s2) { int result = 0; for (Object o1 : s1) if (s2.contains(o1)) result++; return result; } What is the difference between the unbounded wildcard type Set and the raw type Set? Do the question marks really buy you anything? Not to belabor the point, but the wildcard type is safe and the raw type isn’t. You can put any element www.it-ebooks.info CHAPTER 5 GENERICS114 into a collection with a raw type, easily corrupting the collection’s type invariant (as demonstrated by the unsafeAdd method on page 112); you can’t put any ele- ment (other than null) into a Collection. Attempting to do so will gener- ate a compile-time error message like this: WildCard.java:13: cannot find symbol symbol : method add(String) location: interface Collection c.add("verboten"); ^ Admittedly this error message leaves something to be desired, but the com- piler has done its job, preventing you from corrupting the collection’s type invari- ant. Not only can’t you put any element (other than null) into a Collection, but you can’t assume anything about the type of the objects that you get out. If these restrictions are unacceptable, you can use generic methods (Item 27) or bounded wildcard types (Item 28). There are two minor exceptions to the rule that you should not use raw types in new code, both of which stem from the fact that generic type information is erased at runtime (Item 25). You must use raw types in class literals. The speci- fication does not permit the use of parameterized types (though it does permit array types and primitive types) [JLS, 15.8.2]. In other words, List.class, String[].class, and int.class are all legal, but List.class and List.class are not. The second exception to the rule concerns the instanceof operator. Because generic type information is erased at runtime, it is illegal to use the instanceof operator on parameterized types other than unbounded wildcard types. The use of unbounded wildcard types in place of raw types does not affect the behavior of the instanceof operator in any way. In this case, the angle brackets and question marks are just noise. This is the preferred way to use the instanceof operator with generic types: // Legitimate use of raw type - instanceof operator if (o instanceof Set) { // Raw type Set m = (Set) o; // Wildcard type ... } Note that once you’ve determined that o is a Set, you must cast it to the wildcard type Set, not the raw type Set. This is a checked cast, so it will not cause a compiler warning. www.it-ebooks.info ITEM 23: DON’T USE RAW TYPES IN NEW CODE 115 In summary, using raw types can lead to exceptions at runtime, so don’t use them in new code. They are provided only for compatibility and interoperability with legacy code that predates the introduction of generics. As a quick review, Set is a parameterized type representing a set that can contain objects of any type, Set is a wildcard type representing a set that can contain only objects of some unknown type, and Set is a raw type, which opts out of the generic type system. The first two are safe and the last is not. For quick reference, the terms introduced in this item (and a few introduced elsewhere in this chapter) are summarized in the following table: Term Example Item Parameterized type List Item 23 Actual type parameter String Item 23 Generic type List Items 23, 26 Formal type parameter E Item 23 Unbounded wildcard type List Item 23 Raw type List Item 23 Bounded type parameter Item 26 Recursive type bound > Item 27 Bounded wildcard type List Item 28 Generic method static List asList(E[] a) Item 27 Type token String.class Item 29 www.it-ebooks.info CHAPTER 5 GENERICS116 Item 24: Eliminate unchecked warnings When you program with generics, you will see many compiler warnings: unchecked cast warnings, unchecked method invocation warnings, unchecked generic array creation warnings, and unchecked conversion warnings. The more experience you acquire with generics, the fewer warnings you’ll get, but don’t expect newly written code that uses generics to compile cleanly. Many unchecked warnings are easy to eliminate. For example, suppose you accidentally write this declaration: Set exaltation = new HashSet(); The compiler will gently remind you what you did wrong: Venery.java:4: warning: [unchecked] unchecked conversion found : HashSet, required: Set Set exaltation = new HashSet(); ^ You can then make the indicated correction, causing the warning to disappear: Set exaltation = new HashSet(); Some warnings will be much more difficult to eliminate. This chapter is filled with examples of such warnings. When you get warnings that require some thought, persevere! Eliminate every unchecked warning that you can. If you eliminate all warnings, you are assured that your code is typesafe, which is a very good thing. It means that you won’t get a ClassCastException at runtime, and it increases your confidence that your program is behaving as you intended. If you can’t eliminate a warning, and you can prove that the code that provoked the warning is typesafe, then (and only then) suppress the warning with an @SuppressWarnings("unchecked") annotation. If you suppress warn- ings without first proving that the code is typesafe, you are only giving yourself a false sense of security. The code may compile without emitting any warnings, but it can still throw a ClassCastException at runtime. If, however, you ignore unchecked warnings that you know to be safe (instead of suppressing them), you won’t notice when a new warning crops up that represents a real problem. The new warning will get lost amidst all the false alarms that you didn’t silence. www.it-ebooks.info ITEM 24: ELIMINATE UNCHECKED WARNINGS 117 The SuppressWarnings annotation can be used at any granularity, from an individual local variable declaration to an entire class. Always use the Suppress- Warnings annotation on the smallest scope possible. Typically this will be a variable declaration or a very short method or constructor. Never use Suppress- Warnings on an entire class. Doing so could mask critical warnings. If you find yourself using the SuppressWarnings annotation on a method or constructor that’s more than one line long, you may be able to move it onto a local variable declaration. You may have to declare a new local variable, but it’s worth it. For example, consider this toArray method, which comes from ArrayList: public T[] toArray(T[] a) { if (a.length < size) return (T[]) Arrays.copyOf(elements, size, a.getClass()); System.arraycopy(elements, 0, a, 0, size); if (a.length > size) a[size] = null; return a; } If you compile ArrayList, the method generates this warning: ArrayList.java:305: warning: [unchecked] unchecked cast found : Object[], required: T[] return (T[]) Arrays.copyOf(elements, size, a.getClass()); ^ It is illegal to put a SuppressWarnings annotation on the return statement, because it isn’t a declaration [JLS, 9.7]. You might be tempted to put the annota- tion on the entire method, but don’t. Instead, declare a local variable to hold the return value and annotate its declaration, like so: // Adding local variable to reduce scope of @SuppressWarnings public T[] toArray(T[] a) { if (a.length < size) { // This cast is correct because the array we're creating // is of the same type as the one passed in, which is T[]. @SuppressWarnings("unchecked") T[] result = (T[]) Arrays.copyOf(elements, size, a.getClass()); return result; } System.arraycopy(elements, 0, a, 0, size); if (a.length > size) a[size] = null; return a; } www.it-ebooks.info CHAPTER 5 GENERICS118 This method compiles cleanly and minimizes the scope in which unchecked warn- ings are suppressed. Every time you use an @SuppressWarnings("unchecked") annotation, add a comment saying why it’s safe to do so. This will help others understand the code, and more importantly, it will decrease the odds that someone will mod- ify the code so as to make the computation unsafe. If you find it hard to write such a comment, keep thinking. You may end up figuring out that the unchecked opera- tion isn’t safe after all. In summary, unchecked warnings are important. Don’t ignore them. Every unchecked warning represents the potential for a ClassCastException at run- time. Do your best to eliminate these warnings. If you can’t eliminate an unchecked warning and you can prove that the code that provoked it is typesafe, suppress the warning with an @SuppressWarnings("unchecked") annotation in the narrowest possible scope. Record the rationale for your decision to suppress the warning in a comment. www.it-ebooks.info ITEM 25: PREFER LISTS TO ARRAYS 119 Item 25: Prefer lists to arrays Arrays differ from generic types in two important ways. First, arrays are covariant. This scary-sounding word means simply that if Sub is a subtype of Super, then the array type Sub[] is a subtype of Super[]. Generics, by contrast, are invariant: for any two distinct types Type1 and Type2, List is neither a subtype nor a supertype of List [JLS, 4.10; Naftalin07, 2.5]. You might think this means that generics are deficient, but arguably it is arrays that are deficient. This code fragment is legal: // Fails at runtime! Object[] objectArray = new Long[1]; objectArray[0] = "I don't fit in"; // Throws ArrayStoreException but this one is not: // Won't compile! List ol = new ArrayList(); // Incompatible types ol.add("I don't fit in"); Either way you can’t put a String into a Long container, but with an array you find out that you’ve made a mistake at runtime; with a list, you find out at compile time. Of course you’d rather find out at compile time. The second major difference between arrays and generics is that arrays are reified [JLS, 4.7]. This means that arrays know and enforce their element types at runtime. As noted above, if you try to store a String into an array of Long, you’ll get an ArrayStoreException. Generics, by contrast, are implemented by erasure [JLS, 4.6]. This means that they enforce their type constraints only at compile time and discard (or erase) their element type information at runtime. Erasure is what allows generic types to interoperate freely with legacy code that does not use generics (Item 23). Because of these fundamental differences, arrays and generics do not mix well. For example, it is illegal to create an array of a generic type, a parameterized type, or a type parameter. None of these array creation expressions are legal: new List[], new List[], new E[]. All will result in generic array cre- ation errors at compile time. Why is it illegal to create a generic array? Because it isn’t typesafe. If it were legal, casts generated by the compiler in an otherwise correct program could fail at runtime with a ClassCastException. This would violate the fundamental guaran- tee provided by the generic type system. www.it-ebooks.info CHAPTER 5 GENERICS120 To make this more concrete, consider the following code fragment: // Why generic array creation is illegal - won't compile! List[] stringLists = new List[1]; // (1) List intList = Arrays.asList(42); // (2) Object[] objects = stringLists; // (3) objects[0] = intList; // (4) String s = stringLists[0].get(0); // (5) Let’s pretend that line 1, which creates a generic array, is legal. Line 2 creates and initializes a List containing a single element. Line 3 stores the List array into an Object array variable, which is legal because arrays are covariant. Line 4 stores the List into the sole element of the Object array, which succeeds because generics are implemented by erasure: the runtime type of a List instance is simply List, and the runtime type of a List[] instance is List[], so this assignment doesn’t generate an ArrayStoreException. Now we’re in trouble. We’ve stored a List instance into an array that is declared to hold only List instances. In line 5, we retrieve the sole element from the sole list in this array. The compiler automatically casts the retrieved element to String, but it’s an Integer, so we get a ClassCastException at runtime. In order to prevent this from happening, line 1 (which creates a generic array) generates a compile-time error. Types such as E, List, and List are technically known as non- reifiable types [JLS, 4.7]. Intuitively speaking, a non-reifiable type is one whose runtime representation contains less information than its compile-time representa- tion. The only parameterized types that are reifiable are unbounded wildcard types such as List and Map (Item 23). It is legal, though infrequently useful, to create arrays of unbounded wildcard types. The prohibition on generic array creation can be annoying. It means, for exam- ple, that it’s not generally possible for a generic type to return an array of its ele- ment type (but see Item 29 for a partial solution). It also means that you can get confusing warnings when using varargs methods (Item 42) in combination with generic types. This is because every time you invoke a varargs method, an array is created to hold the varargs parameters. If the element type of this array is not reifi- able, you get a warning. There is little you can do about these warnings other than to suppress them (Item 24), and to avoid mixing generics and varargs in your APIs. When you get a generic array creation error, the best solution is often to use the collection type List in preference to the array type E[]. You might sacri- fice some performance or conciseness, but in exchange you get better type safety and interoperability. www.it-ebooks.info ITEM 25: PREFER LISTS TO ARRAYS 121 For example, suppose you have a synchronized list (of the sort returned by Collections.synchronizedList) and a function that takes two values of the type held by the list and returns a third. Now suppose you want to write a method to “reduce” the list by applying the function across it. If the list contains integers and the function adds two integer values, the reduce method returns the sum of all the values in the list. If the function multiplies two integer values, the method returns the product of the values in the list. If the list contains strings and the func- tion concatenates two strings, the method returns a string consisting of all the strings in the list in sequence. In addition to a list and a function, the reduce method takes an initial value for the reduction, which is returned if the list is empty. (The initial value is typically the identity element for the function, which is 0 for addition, 1 for multiplication, and "" for string concatenation.) Here’s how the code might have looked without generics: // Reduction without generics, and with concurrency flaw! static Object reduce(List list, Function f, Object initVal) { synchronized(list) { Object result = initVal; for (Object o : list) result = f.apply(result, o); return result; } } interface Function { Object apply(Object arg1, Object arg2); } Now, suppose you’ve read Item 67, which tells you not to call “alien methods” from a synchronized region. So, you modify the reduce method to copy the con- tents of the list while holding the lock, which allows you to perform the reduction on the copy. Prior to release 1.5, the natural way to do this would have been using List’s toArray method (which locks the list internally): // Reduction without generics or concurrency flaw static Object reduce(List list, Function f, Object initVal) { Object[] snapshot = list.toArray(); // Locks list internally Object result = initVal; for (Object o : list) result = f.apply(result, o); return result; } www.it-ebooks.info CHAPTER 5 GENERICS122 If you try to do this with generics you’ll get into trouble of the sort that we discussed above. Here’s a generic version of the Function interface: interface Function { T apply(T arg1, T arg2); } And here’s a naive attempt to apply generics to the revised version of the reduce method. This is a generic method (Item 27). Don’t worry if you don’t understand the declaration. For the purposes of this item, you should concentrate on the method body: // Naive generic version of reduction - won't compile! static E reduce(List list, Function f, E initVal) { E[] snapshot = list.toArray(); // Locks list E result = initVal; for (E e : snapshot) result = f.apply(result, e); return result; } If you try to compile this method, you’ll get the following error: Reduce.java:12: incompatible types found : Object[], required: E[] E[] snapshot = list.toArray(); // Locks list ^ No big deal, you say, I’ll cast the Object array to an E array: E[] snapshot = (E[]) list.toArray(); That gets rid of the error, but now you get a warning: Reduce.java:12: warning: [unchecked] unchecked cast found : Object[], required: E[] E[] snapshot = (E[]) list.toArray(); // Locks list ^ The compiler is telling you that it can’t check the safety of the cast at runtime because it doesn’t know what E is at runtime—remember, element type informa- tion is erased from generics at runtime. Will this program work? Yes, it turns out that it will, but it isn’t safe. With minor modifications, you could get it to throw a www.it-ebooks.info ITEM 25: PREFER LISTS TO ARRAYS 123 ClassCastException on a line that doesn’t contain an explicit cast. The compile- time type of snapshot is E[] which could be String[], Integer[], or any other kind of array. The runtime type is Object[], and that’s dangerous. Casts to arrays of non-reifiable types should be used only under special circumstances (Item 26). So what should you do? Use a list instead of an array. Here is a version of the reduce method that compiles without error or warning: // List-based generic reduction static E reduce(List list, Function f, E initVal) { List snapshot; synchronized(list) { snapshot = new ArrayList(list); } E result = initVal; for (E e : snapshot) result = f.apply(result, e); return result; } This version is a tad more verbose than the array version, but it’s worth it for the peace of mind that comes from knowing you won’t get a ClassCastException at runtime. In summary, arrays and generics have very different type rules. Arrays are covariant and reified; generics are invariant and erased. As a consequence, arrays provide runtime type safety but not compile-time type safety and vice versa for generics. Generally speaking, arrays and generics don’t mix well. If you find yourself mixing them and getting compile-time errors or warnings, your first impulse should be to replace the arrays with lists. www.it-ebooks.info CHAPTER 5 GENERICS124 Item 26: Favor generic types It is generally not too difficult to parameterize your collection declarations and make use of the generic types and methods provided by the JDK. Writing your own generic types is a bit more difficult, but it’s worth the effort to learn how. Consider the simple stack implementation from Item 6: // Object-based collection - a prime candidate for generics public class Stack { private Object[] elements; private int size = 0; private static final int DEFAULT_INITIAL_CAPACITY = 16; public Stack() { elements = new Object[DEFAULT_INITIAL_CAPACITY]; } public void push(Object e) { ensureCapacity(); elements[size++] = e; } public Object pop() { if (size == 0) throw new EmptyStackException(); Object result = elements[--size]; elements[size] = null; // Eliminate obsolete reference return result; } public boolean isEmpty() { return size == 0; } private void ensureCapacity() { if (elements.length == size) elements = Arrays.copyOf(elements, 2 * size + 1); } } This class is a prime candidate for generification, in other words, for being com- patibly enhanced to take advantage of generic types. As it stands, you have to cast objects that are popped off the stack, and those casts might fail at runtime. The first step in generifying a class is to add one or more type parameters to its decla- www.it-ebooks.info ITEM 26: FAVOR GENERIC TYPES 125 ration. In this case there is one type parameter, representing the element type of the stack, and the conventional name for this parameter is E (Item 44). The next step is to replace all the uses of the type Object with the appropriate type parameter, and then try to compile the resulting program: // Initial attempt to generify Stack = won’t compile! public class Stack { private E[] elements; private int size = 0; private static final int DEFAULT_INITIAL_CAPACITY = 16; public Stack() { elements = new E[DEFAULT_INITIAL_CAPACITY]; } public void push(E e) { ensureCapacity(); elements[size++] = e; } public E pop() { if (size==0) throw new EmptyStackException(); E result = elements[--size]; elements[size] = null; // Eliminate obsolete reference return result; } ... // no changes in isEmpty or ensureCapacity } You’ll generally get at least one error or warning, and this class is no exception. Luckily, this class generates only one error: Stack.java:8: generic array creation elements = new E[DEFAULT_INITIAL_CAPACITY]; ^ As explained in Item 25, you can’t create an array of a non-reifiable type, such as E. This problem arises every time you write a generic type that is backed by an array. There are two ways to solve it. The first solution directly circumvents the prohibition on generic array creation: create an array of Object and cast it to the www.it-ebooks.info CHAPTER 5 GENERICS126 generic array type. Now in place of an error, the compiler will emit a warning. This usage is legal, but it’s not (in general) typesafe: Stack.java:8: warning: [unchecked] unchecked cast found : Object[], required: E[] elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY]; ^ The compiler may not be able to prove that your program is typesafe, but you can. You must convince yourself that the unchecked cast will not compromise the type safety of the program. The array in question (elements) is stored in a private field and never returned to the client or passed to any other method. The only ele- ments stored in the array are those passed to the push method, which are of type E, so the unchecked cast can do no harm. Once you’ve proved that an unchecked cast is safe, suppress the warning in as narrow a scope as possible (Item 24). In this case, the constructor contains only the unchecked array creation, so it’s appropriate to suppress the warning in the entire constructor. With the addition of an annotation to do this, Stack compiles cleanly and you can use it without explicit casts or fear of a ClassCastException: // The elements array will contain only E instances from push(E). // This is sufficient to ensure type safety, but the runtime // type of the array won't be E[]; it will always be Object[]! @SuppressWarnings("unchecked") public Stack() { elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY]; } The second way to eliminate the generic array creation error in Stack is to change the type of the field elements from E[] to Object[]. If you do this, you’ll get a different error: Stack.java:19: incompatible types found : Object, required: E E result = elements[--size]; ^ You can change this error into a warning by casting the element retrieved from the array from Object to E: Stack.java:19: warning: [unchecked] unchecked cast found : Object, required: E E result = (E) elements[--size]; ^ www.it-ebooks.info ITEM 26: FAVOR GENERIC TYPES 127 Because E is a non-reifiable type, there’s no way the compiler can check the cast at runtime. Again, you can easily prove to yourself that the unchecked cast is safe, so it’s appropriate to suppress the warning. In line with the advice of Item 24, we suppress the warning only on the assignment that contains the unchecked cast, not on the entire pop method: // Appropriate suppression of unchecked warning public E pop() { if (size==0) throw new EmptyStackException(); // push requires elements to be of type E, so cast is correct @SuppressWarnings("unchecked") E result = (E) elements[--size]; elements[size] = null; // Eliminate obsolete reference return result; } Which of the two techniques you choose for dealing with the generic array creation error is largely a matter of taste. All other things being equal, it is riskier to suppress an unchecked cast to an array type than to a scalar type, which would suggest the second solution. But in a more realistic generic class than Stack, you would probably be reading from the array at many points in the code, so choosing the second solution would require many casts to E rather than a single cast to E[], which is why the first solution is used more commonly [Naftalin07, 6.7]. The following program demonstrates the use of our generic Stack class. The program prints its command line arguments in reverse order and converted to uppercase. No explicit cast is necessary to invoke String’s toUpperCase method on the elements popped from the stack, and the automatically generated cast is guaranteed to succeed: // Little program to exercise our generic Stack public static void main(String[] args) { Stack stack = new Stack(); for (String arg : args) stack.push(arg); while (!stack.isEmpty()) System.out.println(stack.pop().toUpperCase()); } www.it-ebooks.info CHAPTER 5 GENERICS128 The foregoing example may appear to contradict Item 25, which encourages the use of lists in preference to arrays. It is not always possible or desirable to use lists inside your generic types. Java doesn’t support lists natively, so some generic types, such as ArrayList, must be implemented atop arrays. Other generic types, such as HashMap, are implemented atop arrays for performance. The great majority of generic types are like our Stack example in that their type parameters have no restrictions: you can create a Stack, Stack, Stack>, or a Stack of any other object reference type. Note that you can’t create a Stack of a primitive type: trying to create a Stack or Stack will result in a compile-time error. This is a fun- damental limitation of Java’s generic type system. You can work around this restriction by using boxed primitive types (Item 49). There are some generic types that restrict the permissible values of their type parameters. For example, consider java.util.concurrent.DelayQueue, whose declaration looks like this: class DelayQueue implements BlockingQueue; The type parameter list () requires that the actual type parameter E must be a subtype of java.util.concurrent.Delayed. This allows the DelayQueue implementation and its clients to take advantage of Delayed methods on the elements of a DelayQueue, without the need for explicit casting or the risk of a ClassCastException. The type parameter E is known as a bounded type parameter. Note that the subtype relation is defined so that every type is a subtype of itself [JLS, 4.10], so it is legal to create a DelayQueue. In summary, generic types are safer and easier to use than types that require casts in client code. When you design new types, make sure that they can be used without such casts. This will often mean making the types generic. Generify your existing types as time permits. This will make life easier for new users of these types without breaking existing clients (Item 23). www.it-ebooks.info ITEM 27: FAVOR GENERIC METHODS 129 Item 27: Favor generic methods Just as classes can benefit from generification, so can methods. Static utility meth- ods are particularly good candidates for generification. All of the “algorithm” methods in Collections (such as binarySearch and sort) have been generified. Writing generic methods is similar to writing generic types. Consider this method, which returns the union of two sets: // Uses raw types - unacceptable! (Item 23) public static Set union(Set s1, Set s2) { Set result = new HashSet(s1); result.addAll(s2); return result; } This method compiles, but with two warnings: Union.java:5: warning: [unchecked] unchecked call to HashSet(Collection) as a member of raw type HashSet Set result = new HashSet(s1); ^ Union.java:6: warning: [unchecked] unchecked call to addAll(Collection) as a member of raw type Set result.addAll(s2); ^ To fix these warnings and make the method typesafe, modify the method dec- laration to declare a type parameter representing the element type for the three sets (two arguments and the return value) and use the type parameter in the method. The type parameter list, which declares the type parameter, goes between the method’s modifiers and its return type. In this example, the type parameter list is and the return type is Set. The naming conventions for type parameters are the same for generic methods as for generic types (Items 26, 44): // Generic method public static Set union(Set s1, Set s2) { Set result = new HashSet(s1); result.addAll(s2); return result; } At least for simple generic methods, that’s all there is to it. Now the method compiles without generating any warnings and provides type safety as well as ease www.it-ebooks.info CHAPTER 5 GENERICS130 of use. Here’s a simple program to exercise our method. The program contains no casts and compiles without errors or warnings: // Simple program to exercise generic method public static void main(String[] args) { Set guys = new HashSet( Arrays.asList("Tom", "Dick", "Harry")); Set stooges = new HashSet( Arrays.asList("Larry", "Moe", "Curly")); Set aflCio = union(guys, stooges); System.out.println(aflCio); } When you run the program, it prints [Moe, Harry, Tom, Curly, Larry, Dick]. The order of the elements is implementation-dependent. A limitation of the union method is that the types of all three sets (both input parameters and the return value) have to be the same. You can make the method more flexible by using bounded wildcard types (Item 28). One noteworthy feature of generic methods is that you needn’t specify the value of the type parameter explicitly as you must when invoking generic con- structors. The compiler figures out the value of the type parameters by examining the types of the method arguments. In the case of the program above, the compiler sees that both arguments to union are of type Set, so it knows that the type parameter E must be String. This process is called type inference. As discussed in Item 1, you can exploit the type inference provided by generic method invocation to ease the process of creating parameterized type instances. To refresh your memory, the need to pass the values of type parameters explicitly when invoking generic constructors can be annoying. The type parameters appear redundantly on the left- and right-hand sides of variable declarations: // Parameterized type instance creation with constructor Map> anagrams = new HashMap>(); To eliminate this redundancy, write a generic static factory method corre- sponding to each constructor that you want to use. For example, here is a generic static factory method corresponding to the parameterless HashMap constructor: // Generic static factory method public static HashMap newHashMap() { return new HashMap(); } www.it-ebooks.info ITEM 27: FAVOR GENERIC METHODS 131 With this generic static factory method, you can replace the repetitious declaration above with this concise one: // Parameterized type instance creation with static factory Map> anagrams = newHashMap(); It would be nice if the language did the same kind of type inference when invoking constructors on generic types as it does when invoking generic methods. Someday it might, but as of release 1.6, it does not. A related pattern is the generic singleton factory. On occasion, you will need to create an object that is immutable but applicable to many different types. Because generics are implemented by erasure (Item 25), you can use a single object for all required type parameterizations, but you need to write a static fac- tory method to repeatedly dole out the object for each requested type parameter- ization. This pattern is most frequently used for function objects (Item 21) such as Collections.reverseOrder, but it is also used for collections such as Collec- tions.emptySet. Suppose you have an interface that describes a function that accepts and returns a value of some type T: public interface UnaryFunction { T apply(T arg); } Now suppose that you want to provide an identity function. It would be wasteful to create a new one each time it’s required, as it’s stateless. If generics were rei- fied, you would need one identity function per type, but since they’re erased you need only a generic singleton. Here’s how it looks: // Generic singleton factory pattern private static UnaryFunction IDENTITY_FUNCTION = new UnaryFunction() { public Object apply(Object arg) { return arg; } }; // IDENTITY_FUNCTION is stateless and its type parameter is // unbounded so it's safe to share one instance across all types. @SuppressWarnings("unchecked") public static UnaryFunction identityFunction() { return (UnaryFunction) IDENTITY_FUNCTION; } www.it-ebooks.info CHAPTER 5 GENERICS132 The cast of IDENTITY_FUNCTION to (UnaryFunction) generates an unchecked cast warning, as UnaryFunction is not a UnaryFunction for every T. But the identity function is special: it returns its argument unmodified, so we know that it is typesafe to use it as a UnaryFunction whatever the value of T. Therefore, we can confidently suppress the unchecked cast warning that is generated by this cast. Once we’ve done this, the code compiles without error or warning. Here is a sample program that uses our generic singleton as a UnaryFunc- tion and a UnaryFunction. As usual, it contains no casts and compiles without errors or warnings: // Sample program to exercise generic singleton public static void main(String[] args) { String[] strings = { "jute", "hemp", "nylon" }; UnaryFunction sameString = identityFunction(); for (String s : strings) System.out.println(sameString.apply(s)); Number[] numbers = { 1, 2.0, 3L }; UnaryFunction sameNumber = identityFunction(); for (Number n : numbers) System.out.println(sameNumber.apply(n)); } It is permissible, though relatively rare, for a type parameter to be bounded by some expression involving that type parameter itself. This is what’s known as a recursive type bound. The most common use of recursive type bounds is in con- nection with the Comparable interface, which defines a type’s natural ordering: public interface Comparable { int compareTo(T o); } The type parameter T defines the type to which elements of the type implementing Comparable can be compared. In practice, nearly all types can be compared only to elements of their own type. So, for example, String implements Compa- rable, Integer implements Comparable, and so on. There are many methods that take a list of elements that implement Compara- ble, in order to sort the list, search within it, calculate its minimum or maximum, and the like. To do any of these things, it is required that every element in the list www.it-ebooks.info ITEM 27: FAVOR GENERIC METHODS 133 be comparable to every other element in the list, in other words, that the elements of the list be mutually comparable. Here is how to express that constraint: // Using a recursive type bound to express mutual comparability public static > T max(List list) {...} The type bound > may be read as “for every type T that can be compared to itself,” which corresponds more or less exactly to the notion of mutual comparability. Here is a method to go with the declaration above. It calculates the maximum value of a list according to its elements’ natural order, and it compiles without errors or warnings: // Returns the maximum value in a list - uses recursive type bound public static > T max(List list) { Iterator i = list.iterator(); T result = i.next(); while (i.hasNext()) { T t = i.next(); if (t.compareTo(result) > 0) result = t; } return result; } Recursive type bounds can get much more complex than this, but luckily it doesn’t happen too often. If you understand this idiom, and its wildcard variant (Item 28), you’ll be able to deal with many of the recursive type bounds that you see in practice. In summary, generic methods, like generic types, are safer and easier to use than methods that require their clients to cast input parameters and return values. Like types, you should make sure that your new methods can be used without casts, which will often mean making them generic. And like types, you should generify your existing methods to make life easier for new users without breaking existing clients (Item 23). www.it-ebooks.info CHAPTER 5 GENERICS134 Item 28: Use bounded wildcards to increase API flexibility As noted in Item 25, parameterized types are invariant. In other words, for any two distinct types Type1 and Type2, List is neither a subtype nor a supertype of List. While it is counterintuitive that List is not a subtype of List, it really does make sense. You can put any object into a List, but you can put only strings into a List. Sometimes you need more flexibility than invariant typing can provide. Con- sider the stack from Item 26. To refresh your memory, here is its public API: public class Stack { public Stack(); public void push(E e); public E pop(); public boolean isEmpty(); } Suppose we want to add a method that takes a sequence of elements and pushes them all onto the stack. Here’s a first attempt: // pushAll method without wildcard type - deficient! public void pushAll(Iterable src) { for (E e : src) push(e); } This method compiles cleanly, but it isn’t entirely satisfactory. If the element type of the Iterable src exactly matches that of the stack, it works fine. But suppose you have a Stack and you invoke push(intVal), where intVal is of type Integer. This works, because Integer is a subtype of Number. So logically, it seems that this should work, too: Stack numberStack = new Stack(); Iterable integers = ... ; numberStack.pushAll(integers); If you try it, however, you’ll get this error message because, as noted above, parameterized types are invariant: StackTest.java:7: pushAll(Iterable) in Stack cannot be applied to (Iterable) numberStack.pushAll(integers); ^ www.it-ebooks.info ITEM 28: USE BOUNDED WILDCARDS TO INCREASE API FLEXIBILITY 135 Luckily, there’s a way out. The language provides a special kind of parameter- ized type call a bounded wildcard type to deal with situations like this. The type of the input parameter to pushAll should not be “Iterable of E” but “Iterable of some subtype of E,” and there is a wildcard type that means precisely that: Iter- able. (The use of the keyword extends is slightly misleading: recall from Item 26 that subtype is defined so that every type is a subtype of itself, even though it does not extend itself.) Let’s modify pushAll to use this type: // Wildcard type for parameter that serves as an E producer public void pushAll(Iterable src) { for (E e : src) push(e); } With this change, not only does Stack compile cleanly, but so does the client code that wouldn’t compile with the original pushAll declaration. Because Stack and its client compile cleanly, you know that everything is typesafe. Now suppose you want to write a popAll method to go with pushAll. The popAll method pops each element off the stack and adds the elements to the given collection. Here’s how a first attempt at writing the popAll method might look: // popAll method without wildcard type - deficient! public void popAll(Collection dst) { while (!isEmpty()) dst.add(pop()); } Again, this compiles cleanly and works fine if the element type of the destination collection exactly matches that of the stack. But again, it doesn’t seem entirely satisfactory. Suppose you have a Stack and variable of type Object. If you pop an element from the stack and store it in the variable, it compiles and runs without error. So shouldn’t you be able to do this, too? Stack numberStack = new Stack(); Collection objects = ... ; numberStack.popAll(objects); If you try to compile this client code against the version of popAll above, you’ll get an error very similar to the one that we got with our first version of pushAll: Collection is not a subtype of Collection. Once again, wildcard types provide a way out. The type of the input parameter to popAll www.it-ebooks.info CHAPTER 5 GENERICS136 should not be “collection of E” but “collection of some supertype of E” (where supertype is defined such that E is a supertype of itself [JLS, 4.10]). Again, there is a wildcard type that means precisely that: Collection. Let’s modify popAll to use it: // Wildcard type for parameter that serves as an E consumer public void popAll(Collection dst) { while (!isEmpty()) dst.add(pop()); } With this change, both Stack and the client code compile cleanly. The lesson is clear. For maximum flexibility, use wildcard types on input parameters that represent producers or consumers. If an input parameter is both a producer and a consumer, then wildcard types will do you no good: you need an exact type match, which is what you get without any wildcards. Here is a mnemonic to help you remember which wildcard type to use: PECS stands for producer-extends, consumer-super. In other words, if a parameterized type represents a T producer, use ; if it represents a T consumer, use . In our Stack example, pushAll’s src parameter produces E instances for use by the Stack, so the appropriate type for src is Iterable; popAll’s dst parameter consumes E instances from the Stack, so the appropriate type for dst is Collection. The PECS mnemonic captures the fundamental principle that guides the use of wild- card types. Naftalin and Wadler call it the Get and Put Principle [Naftalin07, 2.4]. With this mnemonic in mind, let’s take a look at some method declarations from previous items. The reduce method in Item 25 has this declaration: static E reduce(List list, Function f, E initVal) Although lists can both consume and produce values, the reduce method uses its list parameter only as an E producer, so its declaration should use a wildcard type that extends E. The parameter f represents a function that both consumes and produces E instances, so a wildcard type would be inappropriate for it. Here’s the resulting method declaration: // Wildcard type for parameter that serves as an E producer static E reduce(List list, Function f, E initVal) www.it-ebooks.info ITEM 28: USE BOUNDED WILDCARDS TO INCREASE API FLEXIBILITY 137 And would this change make any difference in practice? As it turns out, it would. Suppose you have a List, and you want to reduce it with a Function. This would not compile with the original declaration, but it does once you add the bounded wildcard type. Now let’s look at the union method from Item 27. Here is the declaration: public static Set union(Set s1, Set s2) Both parameters, s1 and s2, are E producers, so the PECS mnemonic tells us that the declaration should be: public static Set union(Set s1, Set s2) Note that the return type is still Set. Do not use wildcard types as return types. Rather than providing additional flexibility for your users, it would force them to use wildcard types in client code. Properly used, wildcard types are nearly invisible to users of a class. They cause methods to accept the parameters they should accept and reject those they should reject. If the user of a class has to think about wildcard types, there is probably something wrong with the class’s API. Unfortunately, the type inference rules are quite complex. They take up six- teen pages in the language specification [JLS,–8], and they don’t always do what you want them to. Looking at the revised declaration for union, you might think that you could do this: Set integers = ... ; Set doubles = ... ; Set numbers = union(integers, doubles); If you try it you’ll get this error message: Union.java:14: incompatible types found : Set>> required: Set Set numbers = union(integers, doubles); ^ Luckily there is a way to deal with this sort of error. If the compiler doesn’t infer the type that you wish it had, you can tell it what type to use with an explicit www.it-ebooks.info CHAPTER 5 GENERICS138 type parameter. This is not something that you have to do very often, which is a good thing, as explicit type parameters aren’t very pretty. With the addition of this explicit type parameter, the program compiles cleanly: Set numbers = Union.union(integers, doubles); Next let’s turn our attention to the max method from Item 27. Here is the orig- inal declaration: public static > T max(List list) Here is a revised declaration that uses wildcard types: public static > T max( List list) To get the revised declaration from the original one, we apply the PECS trans- formation twice. The straightforward application is to the parameter list. It pro- duces T instances, so we change the type from List to List. The tricky application is to the type parameter T. This is the first time we’ve seen a wildcard applied to a type parameter. T was originally specified to extend Compa- rable, but a comparable of T consumes T instances (and produces integers indicating order relations). Therefore the parameterized type Comparable is replaced by the bounded wildcard type Comparable. Comparables are always consumers, so you should always use Comparable in prefer- ence to Comparable. The same is true of comparators, so you should always use Comparator in preference to Comparator. The revised max declaration is probably the most complex method declaration in the entire book. Does the added complexity really buy you anything? Yes, it does. Here is a simple example of a list that would be excluded by the original declaration but is permitted by the revised one: List> scheduledFutures = ... ; The reason that you can’t apply the original method declaration to this list is that java.util.concurrent.ScheduledFuture does not implement Compara- ble. Instead, it is a subinterface of Delayed, which extends Comparable. In other words, a ScheduledFuture instance isn’t merely comparable to other ScheduledFuture instances; it’s comparable to any Delayed instance, and that’s enough to cause the original declaration to reject it. www.it-ebooks.info ITEM 28: USE BOUNDED WILDCARDS TO INCREASE API FLEXIBILITY 139 There is one slight problem with the revised declaration for max: it prevents the method from compiling. Here is the method with the revised declaration: // Won’t compile - wildcards can require change in method body! public static > T max( List list) { Iterator i = list.iterator(); T result = i.next(); while (i.hasNext()) { T t = i.next(); if (t.compareTo(result) > 0) result = t; } return result; } Here’s what happens when you try to compile it: Max.java:7: incompatible types found : Iterator required: Iterator Iterator i = list.iterator(); ^ What does this error message mean, and how do we fix the problem? It means that list is not a List, so its iterator method doesn’t return Iterator. It returns an iterator of some subtype of T, so we replace the iterator declaration with this one, which uses a bounded wildcard type: Iterator i = list.iterator(); That is the only change that we have to make to the body of the method. The ele- ments returned by the iterator’s next method are of some subtype of T, so they can be safely stored in a variable of type T. There is one more wildcard-related topic that bears discussing. There is a duality between type parameters and wildcards, and many methods can be declared using one or the other. For example, here are two possible declarations for a static method to swap two indexed items in a list. The first uses an unbounded type parameter (Item 27) and the second an unbounded wildcard: // Two possible declarations for the swap method public static void swap(List list, int i, int j); public static void swap(List list, int i, int j); www.it-ebooks.info CHAPTER 5 GENERICS140 Which of these two declarations is preferable, and why? In a public API, the second is better because it’s simpler. You pass in a list—any list—and the method swaps the indexed elements. There is no type parameter to worry about. As a rule, if a type parameter appears only once in a method declaration, replace it with a wildcard. If it’s an unbounded type parameter, replace it with an unbounded wildcard; if it’s a bounded type parameter, replace it with a bounded wildcard. There’s one problem with the second declaration for swap, which uses a wild- card in preference to a type parameter: the straightforward implementation won’t compile: public static void swap(List list, int i, int j) { list.set(i, list.set(j, list.get(i))); } Trying to compile it produces this less-than-helpful error message: Swap.java:5: set(int,capture#282 of ?) in List cannot be applied to (int,Object) list.set(i, list.set(j, list.get(i))); ^ It doesn’t seem right that we can’t put an element back into the list that we just took it out of. The problem is that the type of list is List, and you can’t put any value except null into a List. Fortunately, there is a way to implement this method without resorting to an unsafe cast or a raw type. The idea is to write a private helper method to capture the wildcard type. The helper method must be a generic method in order to capture the type. Here’s how it looks: public static void swap(List list, int i, int j) { swapHelper(list, i, j); } // Private helper method for wildcard capture private static void swapHelper(List list, int i, int j) { list.set(i, list.set(j, list.get(i))); } The swapHelper method knows that list is a List. Therefore, it knows that any value it gets out of this list is of type E, and that it’s safe to put any value of type E into the list. This slightly convoluted implementation of swap compiles cleanly. It allows us to export the nice wildcard-based declaration of swap, while taking advantage of the more complex generic method internally. Clients of the www.it-ebooks.info ITEM 28: USE BOUNDED WILDCARDS TO INCREASE API FLEXIBILITY 141 swap method don’t have to confront the more complex swapHelper declaration, but they do benefit from it In summary, using wildcard types in your APIs, while tricky, makes the APIs far more flexible. If you write a library that will be widely used, the proper use of wildcard types should be considered mandatory. Remember the basic rule: pro- ducer-extends, consumer-super (PECS). And remember that all comparables and comparators are consumers. www.it-ebooks.info CHAPTER 5 GENERICS142 Item 29: Consider typesafe heterogeneous containers The most common use of generics is for collections, such as Set and Map, and sin- gle-element containers, such as ThreadLocal and AtomicReference. In all of these uses, it is the container that is parameterized. This limits you to a fixed num- ber of type parameters per container. Normally that is exactly what you want. A Set has a single type parameter, representing its element type; a Map has two, rep- resenting its key and value types; and so forth. Sometimes, however, you need more flexibility. For example, a database row can have arbitrarily many columns, and it would be nice to be able to access all of them in a typesafe manner. Luckily, there is an easy way to achieve this effect. The idea is to parameterize the key instead of the container. Then present the parame- terized key to the container to insert or retrieve a value. The generic type system is used to guarantee that the type of the value agrees with its key. As a simple example of this approach, consider a Favorites class that allows its clients to store and retrieve a “favorite” instance of arbitrarily many other classes. The Class object will play the part of the parameterized key. The reason this works is that class Class was generified in release 1.5. The type of a class lit- eral is no longer simply Class, but Class. For example, String.class is of type Class, and Integer.class is of type Class. When a class literal is passed among methods to communicate both compile-time and runtime type information, it is called a type token [Bracha04]. The API for the Favorites class is simple. It looks just like a simple map, except that the key is parameterized instead of the map. The client presents a Class object when setting and getting favorites. Here is the API: // Typesafe heterogeneous container pattern - API public class Favorites { public void putFavorite(Class type, T instance); public T getFavorite(Class type); } Here is a sample program that exercises the Favorites class, storing, retriev- ing, and printing a favorite String, Integer, and Class instance: // Typesafe heterogeneous container pattern - client public static void main(String[] args) { Favorites f = new Favorites(); f.putFavorite(String.class, "Java"); f.putFavorite(Integer.class, 0xcafebabe); f.putFavorite(Class.class, Favorites.class); www.it-ebooks.info ITEM 29: CONSIDER TYPESAFE HETEROGENEOUS CONTAINERS 143 String favoriteString = f.getFavorite(String.class); int favoriteInteger = f.getFavorite(Integer.class); Class favoriteClass = f.getFavorite(Class.class); System.out.printf("%s %x %s%n", favoriteString, favoriteInteger, favoriteClass.getName()); } As you might expect, this program prints Java cafebabe Favorites. A Favorites instance is typesafe: it will never return an Integer when you ask it for a String. It is also heterogeneous: unlike an ordinary map, all the keys are of different types. Therefore, we call Favorites a typesafe heterogeneous container. The implementation of Favorites is surprisingly tiny. Here it is, in its entirety: // Typesafe heterogeneous container pattern - implementation public class Favorites { private Map, Object> favorites = new HashMap, Object>(); public void putFavorite(Class type, T instance) { if (type == null) throw new NullPointerException("Type is null"); favorites.put(type, instance); } public T getFavorite(Class type) { return type.cast(favorites.get(type)); } } There are a few subtle things going on here. Each Favorites instance is backed by a private Map, Object> called favorites. You might think that you couldn’t put anything into this Map because of the unbounded wildcard type, but the truth is quite the opposite. The thing to notice is that the wildcard type is nested: it’s not the type of the Map that’s a wildcard type but the type of its key. This means that every key can have a different parameterized type: one can be Class, the next Class, and so on. That’s where the heteroge- neity comes from. The next thing to notice is that the value type of the favorites Map is simply Object. In other words, the Map does not guarantee the type relationship between keys and values, which is that every value is of the type represented by its key. In fact, Java’s type system is not powerful enough to express this. But we know that it’s true, and we take advantage of it when it comes time to retrieve a favorite. www.it-ebooks.info CHAPTER 5 GENERICS144 The putFavorite implementation is trivial: it simply puts into favorites a mapping from the given Class object to the given favorite instance. As noted, this discards the “type linkage” between the key and the value; it loses the knowledge that the value is an instance of the key. But that’s OK, because the getFavorites method can and does reestablish this linkage. The implementation of the getFavorite method is trickier than that of put- Favorite. First it gets from the favorites map the value corresponding to the given Class object. This is the correct object reference to return, but it has the wrong compile-time type. Its type is simply Object (the value type of the favor- ites map) and we need to return a T. So, the getFavorite implementation dynamically casts the object reference to the type represented by the Class object, using Class’s cast method. The cast method is the dynamic analog of Java’s cast operator. It simply checks that its argument is an instance of the type represented by the Class object. If so, it returns the argument; otherwise it throws a ClassCastException. We know that the cast invocation in getFavorite will never throw ClassCastExcep- tion, assuming the client code compiled cleanly. That is to say, we know that the values in the favorites map always match the types of the keys. So what does the cast method do for us, given that it simply returns its argu- ment? The signature of the cast method takes full advantage of the fact that class Class has been generified. Its return type is the type parameter of the Class object: public class Class { T cast(Object obj); } This is precisely what’s needed by the getFavorite method. It is what allows us to make Favorites typesafe without resorting to an unchecked cast to T. There are two limitations to the Favorites class that are worth noting. First, a malicious client could easily corrupt the type safety of a Favorites instance, sim- ply by using a Class object in its raw form. But the resulting client code would generate an unchecked warning when it was compiled. This is no different from the normal collection implementations such as HashSet and HashMap. You can easily put a String into a HashSet by using the raw type HashSet (Item 23). That said, you can have runtime type safety if you’re willing to pay for it. The way to ensure that Favorites never violates its type invariant is to have the www.it-ebooks.info ITEM 29: CONSIDER TYPESAFE HETEROGENEOUS CONTAINERS 145 putFavorite method check that instance is indeed an instance of the type repre- sented by type. And we already know how to do this. Just use a dynamic cast: // Achieving runtime type safety with a dynamic cast public void putFavorite(Class type, T instance) { favorites.put(type, type.cast(instance)); } There are collection wrappers in java.util.Collections that play the same trick. They are called checkedSet, checkedList, checkedMap, and so forth. Their static factories take a Class object (or two) in addition to a collection (or map). The static factories are generic methods, ensuring that the compile-time types of the Class object and the collection match. The wrappers add reification to the col- lections they wrap. For example, the wrapper throws a ClassCastException at runtime if someone tries to put Coin into your Collection. These wrap- pers are useful for tracking down who adds an incorrectly typed element to a col- lection in an application that mixes generic and legacy code. The second limitation of the Favorites class is that it cannot be used on a non-reifiable type (Item 25). In other words, you can store your favorite String or String[], but not your favorite List. If you try to store your favorite List, your program won’t compile. The reason is that you can’t get a Class object for List: List.class is a syntax error, and it’s a good thing, too. List and List share a single Class object, which is List.class. It would wreak havoc with the internals of a Favorites object if the “type literals” List.class and List.class were legal and returned the same object reference. There is no entirely satisfactory workaround for the second limitation. There is a technique called super type tokens that goes a long way toward addressing the limitation, but this technique has limitations of its own [Gafter07]. The type tokens used by Favorites are unbounded: getFavorite and put- Favorite accept any Class object. Sometimes you may need to limit the types that can be passed to a method. This can be achieved with a bounded type token, which is simply a type token that places a bound on what type can be represented, using a bounded type parameter (Item 27) or a bounded wildcard (Item 28). The annotations API (Item 35) makes extensive use of bounded type tokens. For example, here is the method to read an annotation at runtime. This method www.it-ebooks.info CHAPTER 5 GENERICS146 comes from the AnnotatedElement interface, which is implemented by the reflec- tive types that represent classes, methods, fields, and other program elements: public T getAnnotation(Class annotationType); The argument annotationType is a bounded type token representing an annota- tion type. The method returns the element’s annotation of that type, if it has one, or null, if it doesn’t. In essence, an annotated element is a typesafe heterogeneous container whose keys are annotation types. Suppose you have an object of type Class and you want to pass it to a method that requires a bounded type token, such as getAnnotation. You could cast the object to Class, but this cast is unchecked, so it would generate a compile-time warning (Item 24). Luckily, class Class provides an instance method that performs this sort of cast safely (and dynamically). The method is called asSubclass, and it casts the Class object on which it’s called to represent a subclass of the class represented by its argument. If the cast succeeds, the method returns its argument; if it fails, it throws a ClassCastException. Here’s how you use the asSubclass method to read an annotation whose type is unknown at compile time. This method compiles without error or warning: // Use of asSubclass to safely cast to a bounded type token static Annotation getAnnotation(AnnotatedElement element, String annotationTypeName) { Class annotationType = null; // Unbounded type token try { annotationType = Class.forName(annotationTypeName); } catch (Exception ex) { throw new IllegalArgumentException(ex); } return element.getAnnotation( annotationType.asSubclass(Annotation.class)); } In summary, the normal use of generics, exemplified by the collections APIs, restricts you to a fixed number of type parameters per container. You can get around this restriction by placing the type parameter on the key rather than the container. You can use Class objects as keys for such typesafe heterogeneous containers. A Class object used in this fashion is called a type token. You can also use a custom key type. For example, you could have a DatabaseRow type repre- senting a database row (the container), and a generic type Column as its key. www.it-ebooks.info 147 CHAPTER 6 Enums and Annotations IN release 1.5, two families of reference types were added to the language: a new kind of class called an enum type, and a new kind of interface called an annotation type. This chapter discusses best practices for using these new type families. Item 30: Use enums instead of int constants An enumerated type is a type whose legal values consist of a fixed set of con- stants, such as the seasons of the year, the planets in the solar system, or the suits in a deck of playing cards. Before enum types were added to the language, a com- mon pattern for representing enumerated types was to declare a group of named int constants, one for each member of the type: // The int enum pattern - severely deficient! public static final int APPLE_FUJI = 0; public static final int APPLE_PIPPIN = 1; public static final int APPLE_GRANNY_SMITH = 2; public static final int ORANGE_NAVEL = 0; public static final int ORANGE_TEMPLE = 1; public static final int ORANGE_BLOOD = 2; This technique, known as the int enum pattern, has many shortcomings. It provides nothing in the way of type safety and little in the way of convenience. The compiler won’t complain if you pass an apple to a method that expects an orange, compare apples to oranges with the == operator, or worse: // Tasty citrus flavored applesauce! int i = (APPLE_FUJI - ORANGE_TEMPLE) / APPLE_PIPPIN; www.it-ebooks.info CHAPTER 6 ENUMS AND ANNOTATIONS148 Note that the name of each apple constant is prefixed with APPLE_ and the name of each orange constant is prefixed with ORANGE_. This is because Java doesn’t provide namespaces for int enum groups. Prefixes prevent name clashes when two int enum groups have identically named constants. Programs that use the int enum pattern are brittle. Because int enums are compile-time constants, they are compiled into the clients that use them. If the int associated with an enum constant is changed, its clients must be recompiled. If they aren’t, they will still run, but their behavior will be undefined. There is no easy way to translate int enum constants into printable strings. If you print such a constant or display it from a debugger, all you see is a number, which isn’t very helpful. There is no reliable way to iterate over all the int enum constants in a group, or even to obtain the size of an int enum group. You may encounter a variant of this pattern in which String constants are used in place of int constants. This variant, known as the String enum pattern, is even less desirable. While it does provide printable strings for its constants, it can lead to performance problems because it relies on string comparisons. Worse, it can lead naive users to hard-code string constants into client code instead of using field names. If such a hard-coded string constant contains a typographical error, it will escape detection at compile time and result in bugs at runtime. Luckily, as of release 1.5, the language provides an alternative that avoids the shortcomings of the int and string enum patterns and provides many added ben- efits. It is the (JLS, 8.9). Here’s how it looks in its simplest form: public enum Apple { FUJI, PIPPIN, GRANNY_SMITH } public enum Orange { NAVEL, TEMPLE, BLOOD } On the surface, these enum types may appear similar to those of other languages, such as C, C++, and C#, but appearances are deceiving. Java’s enum types are full-fledged classes, far more powerful than their counterparts in these other lan- guages, where enums are essentially int values. The basic idea behind Java’s enum types is simple: they are classes that export one instance for each enumeration constant via a public static final field. Enum types are effectively final, by virtue of having no accessible constructors. Because clients can neither create instances of an enum type nor extend it, there can be no instances but the declared enum constants. In other words, enum types are instance-controlled (page 6). They are a generalization of singletons (Item 3), which are essentially single-element enums. For readers familiar with the first edi- tion of this book, enum types provide linguistic support for the typesafe enum pat- tern [Bloch01, Item 21]. www.it-ebooks.info ITEM 30: USE ENUMS INSTEAD OF INT CONSTANTS 149 Enums provide compile-time type safety. If you declare a parameter to be of type Apple, you are guaranteed that any non-null object reference passed to the parameter is one of the three valid Apple values. Attempts to pass values of the wrong type will result in compile-time errors, as will attempts to assign an expres- sion of one enum type to a variable of another, or to use the == operator to com- pare values of different enum types. Enum types with identically named constants coexist peacefully because each type has its own namespace. You can add or reorder constants in an enum type without recompiling its clients because the fields that export the constants provide a layer of insulation between an enum type and its clients: the constant values are not compiled into the clients as they are in the int enum pattern. Finally, you can translate enums into printable strings by calling their toString method. In addition to rectifying the deficiencies of int enums, enum types let you add arbitrary methods and fields and implement arbitrary interfaces. They provide high-quality implementations of all the Object methods (Chapter 3), they imple- ment Comparable (Item 12) and Serializable (Chapter 11), and their serialized form is designed to withstand most changes to the enum type. So why would you want to add methods or fields to an enum type? For start- ers, you might want to associate data with its constants. Our Apple and Orange types, for example, might benefit from a method that returns the color of the fruit, or one that returns an image of it. You can augment an enum type with any method that seems appropriate. An enum type can start life as a simple collection of enum constants and evolve over time into a full-featured abstraction. For a nice example of a rich enum type, consider the eight planets of our solar system. Each planet has a mass and a radius, and from these two attributes you can compute its surface gravity. This in turn lets you compute the weight of an object on the planet’s surface, given the mass of the object. Here’s how this enum looks. The numbers in parentheses after each enum constant are parameters that are passed to its constructor. In this case, they are the planet’s mass and radius: // Enum type with data and behavior public enum Planet { MERCURY(3.302e+23, 2.439e6), VENUS (4.869e+24, 6.052e6), EARTH (5.975e+24, 6.378e6), MARS (6.419e+23, 3.393e6), JUPITER(1.899e+27, 7.149e7), SATURN (5.685e+26, 6.027e7), URANUS (8.683e+25, 2.556e7), NEPTUNE(1.024e+26, 2.477e7); www.it-ebooks.info CHAPTER 6 ENUMS AND ANNOTATIONS150 private final double mass; // In kilograms private final double radius; // In meters private final double surfaceGravity; // In m / s^2 // Universal gravitational constant in m^3 / kg s^2 private static final double G = 6.67300E-11; // Constructor Planet(double mass, double radius) { this.mass = mass; this.radius = radius; surfaceGravity = G * mass / (radius * radius); } public double mass() { return mass; } public double radius() { return radius; } public double surfaceGravity() { return surfaceGravity; } public double surfaceWeight(double mass) { return mass * surfaceGravity; // F = ma } } It is easy to write a rich enum type such as Planet. To associate data with enum constants, declare instance fields and write a constructor that takes the data and stores it in the fields. Enums are by their nature immutable, so all fields should be final (Item 15). They can be public, but it is better to make them private and provide public accessors (Item 14). In the case of Planet, the constructor also computes and stores the surface gravity, but this is just an optimization. The grav- ity could be recomputed from the mass and radius each time it was used by the surfaceWeight method, which takes an object’s mass and returns its weight on the planet represented by the constant. While the Planet enum is simple, it is surprisingly powerful. Here is a short program that takes the earth-weight of an object (in any unit) and prints a nice table of the object’s weight on all eight planets (in the same unit): public class WeightTable { public static void main(String[] args) { double earthWeight = Double.parseDouble(args[0]); double mass = earthWeight / Planet.EARTH.surfaceGravity(); for (Planet p : Planet.values()) System.out.printf("Weight on %s is %f%n", p, p.surfaceWeight(mass)); } } www.it-ebooks.info ITEM 30: USE ENUMS INSTEAD OF INT CONSTANTS 151 Note that Planet, like all enums, has a static values method that returns an array of its values in the order they were declared. Note also that the toString method returns the declared name of each enum value, enabling easy printing by println and printf. If you’re dissatisfied with this string representation, you can change it by overriding the toString method. Here is the result of running our little WeightTable program with the command line argument 175: Weight on MERCURY is 66.133672 Weight on VENUS is 158.383926 Weight on EARTH is 175.000000 Weight on MARS is 66.430699 Weight on JUPITER is 442.693902 Weight on SATURN is 186.464970 Weight on URANUS is 158.349709 Weight on NEPTUNE is 198.846116 If this is the first time you’ve seen Java’s printf method in action, note that it differs from C’s in that you use %n where you’d use \n in C. Some behaviors associated with enum constants may need to be used only from within the class or package in which the enum is defined. Such behaviors are best implemented as private or package-private methods. Each constant then car- ries with it a hidden collection of behaviors that allows the class or package con- taining the enum to react appropriately when presented with the constant. Just as with other classes, unless you have a compelling reason to expose an enum method to its clients, declare it private or, if need be, package-private (Item 13). If an enum is generally useful, it should be a top-level class; if its use is tied to a specific top-level class, it should be a member class of that top-level class (Item 22). For example, the java.math.RoundingMode enum represents a rounding mode for decimal fractions. These rounding modes are used by the BigDecimal class, but they provide a useful abstraction that is not fundamentally tied to Big- Decimal. By making RoundingMode a top-level enum, the library designers encourage any programmer who needs rounding modes to reuse this enum, lead- ing to increased consistency across APIs. The techniques demonstrated in the Planet example are sufficient for most enum types, but sometimes you need more. There is different data associated with each Planet constant, but sometimes you need to associate fundamentally differ- ent behavior with each constant. For example, suppose you are writing an enum type to represent the operations on a basic four-function calculator, and you want www.it-ebooks.info CHAPTER 6 ENUMS AND ANNOTATIONS152 to provide a method to perform the arithmetic operation represented by each con- stant. One way to achieve this is to switch on the value of the enum: // Enum type that switches on its own value - questionable public enum Operation { PLUS, MINUS, TIMES, DIVIDE; // Do the arithmetic op represented by this constant double apply(double x, double y) { switch(this) { case PLUS: return x + y; case MINUS: return x - y; case TIMES: return x * y; case DIVIDE: return x / y; } throw new AssertionError("Unknown op: " + this); } } This code works, but is isn’t very pretty. It won’t compile without the throw statement because the end of the method is technically reachable, even though it will never be reached [JLS, 14.2.1]. Worse, the code is fragile. If you add a new enum constant but forget to add a corresponding case to the switch, the enum will still compile, but it will fail at runtime when you try to apply the new operation. Luckily, there is a better way to associate a different behavior with each enum constant: declare an abstract apply method in the enum type, and override it with a concrete method for each constant in a constant-specific class body. Such meth- ods are knows as constant-specific method implementations: // Enum type with constant-specific method implementations public enum Operation { PLUS { double apply(double x, double y){return x + y;} }, MINUS { double apply(double x, double y){return x - y;} }, TIMES { double apply(double x, double y){return x * y;} }, DIVIDE { double apply(double x, double y){return x / y;} }; abstract double apply(double x, double y); } If you add a new constant to the second version of Operation, it is unlikely that you’ll forget to provide an apply method, as the method immediately follows each constant declaration. In the unlikely event that you do forget, the compiler will remind you, as abstract methods in an enum type must be overridden with concrete methods in all of its constants. www.it-ebooks.info ITEM 30: USE ENUMS INSTEAD OF INT CONSTANTS 153 Constant-specific method implementations can be combined with constant- specific data. For example, here is a version of Operation that overrides the toString method to return the symbol commonly associated with the operation: // Enum type with constant-specific class bodies and data public enum Operation { PLUS("+") { double apply(double x, double y) { return x + y; } }, MINUS("-") { double apply(double x, double y) { return x - y; } }, TIMES("*") { double apply(double x, double y) { return x * y; } }, DIVIDE("/") { double apply(double x, double y) { return x / y; } }; private final String symbol; Operation(String symbol) { this.symbol = symbol; } @Override public String toString() { return symbol; } abstract double apply(double x, double y); } In some cases, overriding toString in an enum is very useful. For example, the toString implementation above makes it easy to print arithmetic expressions, as demonstrated by this little program: public static void main(String[] args) { double x = Double.parseDouble(args[0]); double y = Double.parseDouble(args[1]); for (Operation op : Operation.values()) System.out.printf("%f %s %f = %f%n", x, op, y, op.apply(x, y)); } Running this program with 2 and 4 as command line arguments produces the following output: 2.000000 + 4.000000 = 6.000000 2.000000 - 4.000000 = -2.000000 2.000000 * 4.000000 = 8.000000 2.000000 / 4.000000 = 0.500000 www.it-ebooks.info CHAPTER 6 ENUMS AND ANNOTATIONS154 Enum types have an automatically generated valueOf(String) method that translates a constant’s name into the constant itself. If you override the toString method in an enum type, consider writing a fromString method to translate the custom string representation back to the corresponding enum. The following code (with the type name changed appropriately) will do the trick for any enum, so long as each constant has a unique string representation: // Implementing a fromString method on an enum type private static final Map stringToEnum = new HashMap(); static { // Initialize map from constant name to enum constant for (Operation op : values()) stringToEnum.put(op.toString(), op); } // Returns Operation for string, or null if string is invalid public static Operation fromString(String symbol) { return stringToEnum.get(symbol); } Note that the Operation constants are put into the stringToEnum map from a static block that runs after the constants have been created. Trying to make each constant put itself into the map from its own constructor would cause a compila- tion error. This is a good thing, because it would cause a NullPointerException if it were legal. Enum constructors aren’t permitted to access the enum’s static fields, except for compile-time constant fields. This restriction is necessary because these static fields have not yet been initialized when the constructors run. A disadvantage of constant-specific method implementations is that they make it harder to share code among enum constants. For example, consider an enum representing the days of the week in a payroll package. This enum has a method that calculates a worker’s pay for that day given the worker’s base salary (per hour) and the number of hours worked on that day. On the five weekdays, any time worked in excess of a normal shift generates overtime pay; on the two week- end days, all work generates overtime pay. With a switch statement, it’s easy to do this calculation by applying multiple case labels to each of two code fragments. For brevity’s sake, the code in this example uses double, but note that double is not an appropriate data type for a payroll application (Item 48): // Enum that switches on its value to share code - questionable enum PayrollDay { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY; private static final int HOURS_PER_SHIFT = 8; www.it-ebooks.info ITEM 30: USE ENUMS INSTEAD OF INT CONSTANTS 155 double pay(double hoursWorked, double payRate) { double basePay = hoursWorked * payRate; double overtimePay; // Calculate overtime pay switch(this) { case SATURDAY: case SUNDAY: overtimePay = hoursWorked * payRate / 2; default: // Weekdays overtimePay = hoursWorked <= HOURS_PER_SHIFT ? 0 : (hoursWorked - HOURS_PER_SHIFT) * payRate / 2; break; } return basePay + overtimePay; } } This code is undeniably concise, but it is dangerous from a maintenance per- spective. Suppose you add an element to the enum, perhaps a special value to rep- resent a vacation day, but forget to add a corresponding case to the switch statement. The program will still compile, but the pay method will silently pay the worker the same amount for a vacation day as for an ordinary weekday. To perform the pay calculation safely with constant-specific method imple- mentations, you would have to duplicate the overtime pay computation for each constant, or move the computation into two helper methods (one for weekdays and one for weekend days) and invoke the appropriate helper method from each constant. Either approach would result in a fair amount of boilerplate code, sub- stantially reducing readability and increasing the opportunity for error. The boilerplate could be reduced by replacing the abstract overtimePay method on PayrollDay with a concrete method that performs the overtime calcu- lation for weekdays. Then only the weekend days would have to override the method. But this would have the same disadvantage as the switch statement: if you added another day without overriding the overtimePay method, you would silently inherit the weekday calculation. What you really want is to be forced to choose an overtime pay strategy each time you add an enum constant. Luckily, there is a nice way to achieve this. The idea is to move the overtime pay computation into a private nested enum, and to pass an instance of this strategy enum to the constructor for the PayrollDay enum. The PayrollDay enum then delegates the overtime pay calculation to the strategy enum, eliminating the need for a switch statement or constant-specific method www.it-ebooks.info CHAPTER 6 ENUMS AND ANNOTATIONS156 implementation in PayrollDay. While this pattern is less concise than the switch statement, it is safer and more flexible: // The strategy enum pattern enum PayrollDay { MONDAY(PayType.WEEKDAY), TUESDAY(PayType.WEEKDAY), WEDNESDAY(PayType.WEEKDAY), THURSDAY(PayType.WEEKDAY), FRIDAY(PayType.WEEKDAY), SATURDAY(PayType.WEEKEND), SUNDAY(PayType.WEEKEND); private final PayType payType; PayrollDay(PayType payType) { this.payType = payType; } double pay(double hoursWorked, double payRate) { return payType.pay(hoursWorked, payRate); } // The strategy enum type private enum PayType { WEEKDAY { double overtimePay(double hours, double payRate) { return hours <= HOURS_PER_SHIFT ? 0 : (hours - HOURS_PER_SHIFT) * payRate / 2; } }, WEEKEND { double overtimePay(double hours, double payRate) { return hours * payRate / 2; } }; private static final int HOURS_PER_SHIFT = 8; abstract double overtimePay(double hrs, double payRate); double pay(double hoursWorked, double payRate) { double basePay = hoursWorked * payRate; return basePay + overtimePay(hoursWorked, payRate); } } } If switch statements on enums are not a good choice for implementing con- stant-specific behavior on enums, what are they good for? Switches on enums are good for augmenting external enum types with constant-specific behavior. For example, suppose the Operation enum is not under your control, and you www.it-ebooks.info ITEM 30: USE ENUMS INSTEAD OF INT CONSTANTS 157 wish it had an instance method to return the inverse of each operation. You can simulate the effect with the following static method: // Switch on an enum to simulate a missing method public static Operation inverse(Operation op) { switch(op) { case PLUS: return Operation.MINUS; case MINUS: return Operation.PLUS; case TIMES: return Operation.DIVIDE; case DIVIDE: return Operation.TIMES; default: throw new AssertionError("Unknown op: " + op); } } Enums are, generally speaking, comparable in performance to int constants. A minor performance disadvantage of enums over int constants is that there is a space and time cost to load and initialize enum types. Except on resource-con- strained devices, such as cell phones and toasters, this is unlikely to be noticeable in practice. So when should you use enums? Anytime you need a fixed set of constants. Of course, this includes “natural enumerated types,” such as the planets, the days of the week, and the chess pieces. But it also includes other sets for which you know all the possible values at compile time, such as choices on a menu, operation codes, and command line flags. It is not necessary that the set of constants in an enum type stay fixed for all time. The enum feature was specifically designed to allow for binary compatible evolution of enum types. In summary, the advantages of enum types over int constants are compelling. Enums are far more readable, safer, and more powerful. Many enums require no explicit constructors or members, but many others benefit from associating data with each constant and providing methods whose behavior is affected by this data. Far fewer enums benefit from associating multiple behaviors with a single method. In this relatively rare case, prefer constant-specific methods to enums that switch on their own values. Consider the strategy enum pattern if multiple enum constants share common behaviors. www.it-ebooks.info CHAPTER 6 ENUMS AND ANNOTATIONS158 Item 31: Use instance fields instead of ordinals Many enums are naturally associated with a single int value. All enums have an ordinal method, which returns the numerical position of each enum constant in its type. You may be tempted to derive an associated int value from the ordinal: // Abuse of ordinal to derive an associated value - DON'T DO THIS public enum Ensemble { SOLO, DUET, TRIO, QUARTET, QUINTET, SEXTET, SEPTET, OCTET, NONET, DECTET; public int numberOfMusicians() { return ordinal() + 1; } } While this enum works, it is a maintenance nightmare. If the constants are reordered, the numberOfMusicians method will break. If you want to add a sec- ond enum constant associated with an int value that you’ve already used, you’re out of luck. For example, it might be nice to add a constant for double quartet, which, like an octet, consists of eight musicians, but there is no way to do it. Also, you can’t add a constant for an int value without adding constants for all intervening int values. For example, suppose you want to add a constant rep- resenting a triple quartet, which consists of twelve musicians. There is no stan- dard term for an ensemble consisting of eleven musicians, so you are forced to add a dummy constant for the unused int value (11). At best, this is ugly. If many int values are unused, it’s impractical. Luckily, there is a simple solution to these problems. Never derive a value associated with an enum from its ordinal; store it in an instance field instead: public enum Ensemble { SOLO(1), DUET(2), TRIO(3), QUARTET(4), QUINTET(5), SEXTET(6), SEPTET(7), OCTET(8), DOUBLE_QUARTET(8), NONET(9), DECTET(10), TRIPLE_QUARTET(12); private final int numberOfMusicians; Ensemble(int size) { this.numberOfMusicians = size; } public int numberOfMusicians() { return numberOfMusicians; } } The Enum specification has this to say about ordinal: “Most programmers will have no use for this method. It is designed for use by general-purpose enum- based data structures such as EnumSet and EnumMap.” Unless you are writing such a data structure, you are best off avoiding the ordinal method entirely. www.it-ebooks.info ITEM 32: USE ENUMSET INSTEAD OF BIT FIELDS 159 Item 32: Use EnumSet instead of bit fields If the elements of an enumerated type are used primarily in sets, it is traditional to use the int enum pattern (Item 30), assigning a different power of 2 to each con- stant: // Bit field enumeration constants - OBSOLETE! public class Text { public static final int STYLE_BOLD = 1 << 0; // 1 public static final int STYLE_ITALIC = 1 << 1; // 2 public static final int STYLE_UNDERLINE = 1 << 2; // 4 public static final int STYLE_STRIKETHROUGH = 1 << 3; // 8 // Parameter is bitwise OR of zero or more STYLE_ constants public void applyStyles(int styles) { ... } } This representation lets you use the bitwise OR operation to combine several con- stants into a set, known as a bit field: text.applyStyles(STYLE_BOLD | STYLE_ITALIC); The bit field representation also lets you perform set operations such as union and intersection efficiently using bitwise arithmetic. But bit fields have all the dis- advantages of int enum constants and more. It is even harder to interpret a bit field than a simple int enum constant when it is printed as a number. Also, there is no easy way to iterate over all of the elements represented by a bit field. Some programmers who use enums in preference to int constants still cling to the use of bit fields when they need to pass around sets of constants. There is no reason to do this; a better alternative exists. The java.util package provides the EnumSet class to efficiently represent sets of values drawn from a single enum type. This class implements the Set interface, providing all of the richness, type safety, and interoperability you get with any other Set implementation. But inter- nally, each EnumSet is represented as a bit vector. If the underlying enum type has sixty-four or fewer elements—and most do—the entire EnumSet is represented with a single long (page 7), so its performance is comparable to that of a bit field. Bulk operations, such as removeAll and retainAll, are implemented using bit- wise arithmetic, just as you’d do manually for bit fields. But you are insulated from the ugliness and error-proneness of manual bit twiddling: the EnumSet does the hard work for you. www.it-ebooks.info CHAPTER 6 ENUMS AND ANNOTATIONS160 Here is how the previous example looks when modified to use enums instead of bit fields. It is shorter, clearer, and safer: // EnumSet - a modern replacement for bit fields public class Text { public enum Style { BOLD, ITALIC, UNDERLINE, STRIKETHROUGH } // Any Set could be passed in, but EnumSet is clearly best public void applyStyles(Set