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 extends Node> 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, 8.4.8.3]. 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 extends E> 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, 8.4.8.3]. 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 extends E> 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 extends E> 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