280 likes | 452 Views
Effective Java: General Programming. Last Updated: Spring 2009. Agenda. Material From Joshua Bloch Effective Java 2 nd Edition Cover Items 45-56 “General Programming” Chapter Bottom Line: Nuts and bolts of the Java language Treatment of two extralinguistic language facilities
E N D
Effective Java: General Programming Last Updated: Spring 2009
Agenda • Material From Joshua Bloch • Effective Java 2nd Edition • Cover Items 45-56 • “General Programming” Chapter • Bottom Line: • Nuts and bolts of the Java language • Treatment of two extralinguistic language facilities • Optimization and naming conventions
Item 45: Minimize the Scope of Local Variables • Similar to Item 13: “Minimize the accessibility of classes and members” • Some older languages (eg C) require declarations at the beginning of a block • A habit worth breaking • Important Case: Prefer for loops to while loops // Preferred idiom for iterating over a collection for (Element e : C) { doSomething(e); // Note the lack (of an explicit) cast } // No for-each loop or generics before release 1.5 for (Iterator i = c.iterator(); i.hasNext(); ) { doSomething( (Element) i.next()); // Note the cast }
More Item 45: Problems with while Loops • Sample problem with while loops • Problem disappears with local declarations in a for loop // Spot the bug? Iterator<Element> i = c.iterator(); while (i.hasNext()) { doSomething(i.next()); } Iterator<Element> i2 = c2.iterator(); while (i.hasNext()) { doSomething(i2.next()); } BUG! Unfortunately, this bug is silent
More Item 45: Solution with for Loops • Consider the same formulation with for loops • Result is compile time error • Note: with for loop – no reason to change variable names for (Iterator<Element> i = c.iterator(); i.hasNext(); ) { doSomething(i.next()); } // Compile time error – cannot find symbol i for (Iterator<Element> i2 = c2.iterator(); i.hasNext(); ) { doSomething(i2.next()); }
More Item 45: Example of Multiple Initializations • A final for loop example for (int i=0, n = expensiveComputation(); i < n; i++) { doSomething(i); } • Note that there are two loop variables: i and n • Scope of both is limited to for loop • Avoid if expensiveComputation() does not have a constant value
Item 46: Prefer for-each Loops to Traditional for Loops // Preferred idiom for iterating over a collections and arrays for (Element e: elements) { // read “:” as “in” doSomething(e); } // No longer the preferred idiom to iterate over a collection for (Iterator I = c.iterator(); i.hasNext(); ) { doSomething( (Element) i.next()); // No generics before 1.5 } // No longer the preferred idiom to iterate over an array for (int i=0; i < a.length; i++ ) { doSomething( a[i] ); // Note: you still need this idiom if you want write a[i] = … }
More Item 46: Nested Iterations // Can you spot the bug? enum Suit { CLUB, DIAMOND, HEART, SPADE } enum Rank { ACE, DEUCE, …, KING } Collection <Suit> suits = Arrays.asList(Suit.values()); Collection <Rank> rank = Arrays.asList(Rank.values()); List <Card> deck = new ArrayList<Card>(); for (Iterator<Suit> i = suits.iterator(); i.hasNext(); ) { for (Iterator<Rank> j = ranks.iterator(); j.hasNext(); ) { deck.add (new Card(i.next(), j.next()); // throws NoSuchElementException } } // Fixed – but still ugly for (Iterator<Suit> i = suits.iterator(); i.hasNext(); ) { Suit suit = i.next(); for (Iterator<Rank> j = ranks.iterator(); j.hasNext(); ) { deck.add (new Card(suit, j.next()); } }
More Item 46: Using the for-each Loop // For each loop solves problem for (Suit suit : suits ) { for (Rank rank : ranks) { deck.add (new Card(suit, rank); } } // Similar problem, but fails silently enum Face { ONE, TWO, THREE, FOUR, FIVE, SIX } Collection<Face> faces = Arrays.asList(Face.values()); for (Iterator<Face> i = faces.iterator(); i.hasNext()) { for Iterator<Face> j = faces.iterator(); j.hasNext()) { System.out.println(i.next() + “ “ + j.next()); } } // Output is ONE ONE, TWO TWO, etc, instead of all combinations // Same fix for (Face face1 : faces) { for (Face face2 : faces) { System.out.println(face1 + “ “ + face2); } }
More Item 46: Iterable interface • Simple to implement Iterable interface: // public interface Iterable<E> { // Returns an iterator over the elements in this iterable public Iterator<E> iterator(); } • You should provide this API to your clients, if appropriate • Three cases where client can’t use a for-each loop • Filtering – client needs to traverse a collection and remove selected elements • Transforming – client needs to traverse a collection and replace some values • Parallel Iteration – client needs to traverse multiple collections in parallel, and hence need to control iterator or index variable
More Item 46: Implementing and then using Iterable public class IterableExample { public static void main( String[] args) { Example<String> example = new Example<String>(); example.add("hat"); example.add("cat"); for (String s: example) { System.out.println (s); } } } public class Example<E> implements Iterable<E> { Set<E> s = new TreeSet(); public void add (E e) { s.add(e);} public Iterator<E> iterator() { return s.iterator(); } }
Item 47: Know and Use the Libraries // Common, but deeply flawed! private static final Random rnd = new Random(); static int random (int n) { return Math.abs(rnd.nextInt()) % n; } • Three flaws: • if n is a small power of 2, sequence (quickly) repeats • if n is not a power of 2, some numbers are more frequent • See example in Bloch where 2/3 of results show up in half the range • sometimes, can fail catastrophically • What if rnd.nextInt() returns Integer.MIN_VALUE? • What to do? • It’s easy (if you know the libraries) • Use Random.nextInt() - Available since 1.2
More Item 47: Library Advantages • Using a library takes advantage of • Knowledge of experts who wrote it • Experience of users who used it before you • Libraries evolve • Numerous features added to libraries in every major release • Bloch suggests studying java.lang, java.util, and java.io • Don’t reinvent the wheel • Library code is better than your code! • Bloch says it’s not a comment about your abilities (But this is Bloch and peers we’re talking about…) • You don’t have the same resources
Item 48: Avoid float and double if Exact Answers are Required // Broken – uses floating point for monetary calculation! public static void main (String[] args) { double funds = 1.00; int itemsBought = 0; for (double price = .10; funds >= price; price +=10 ) { funds -= price; itemsBought++; } System.out.println ( itemsBought + “ items bought.”); System.out.println ( “Change” $” + funds); } • Results: • 3 items bought • $0.3999999999999999 in change
Item 49: Prefer Primitive Types to Boxed Primitives // Broken comparator – can you spot the flaw? Comparator < Integer> naturalOrder = new Comparator<Integer>() { // Anonymous type public int compare (Integer first, Integer second) { return first < second ? -1 : // Auto unboxing (first == second // No auto unboxing (!) ? 0 : 1); } }; • Sample uses: • naturalOrder.compare(new Integer(41), new Integer(42)); • naturalOrder.compare(new Integer(42), new Integer(42)); naturalOrder.compare(new Integer(43), new Integer(42));
More Item 49 // Repaired version Comparator < Integer> naturalOrder = new Comparator<Integer>() { // Still an anonymous type public int compare (Integer first, Integer second) { int f = first; int s = second; // Auto unboxing return f < s ? -1 : (f == s ? 0 : 1); // No unboxing needed } }; // Another little gem public class Unbelievable { static Integer i; public static void main(String[] args) { (if i == 42) System.out.println(“Unbelievable”); } } • Doesn’t print “Unbelievable” • But does throw NullPointerException! • When mixing primitives and boxed primitives, the boxed primitive is auto unboxed
More Item 49 // Performance problem with autoboxing public static void main(String[] args) { Long sum = 0L; // ok if declaration is “long sum = 0;” for (long i = 0; i < Integer.MAX_VALUE; i++) { sum +=i; } System.out.println(sum); } • Orders of magnitude slower than it should be • So, when to use boxed primitives? • As elements, keys, and values in Collections • Otherwise, use primitives
Item 50: Avoid Strings Where Other Types Are More Appropriate • Strings are a poor substitute for other value types • Input arrives as String from file, keyboard or network • Transform to underlying type, eg int, float, or boolean • Strings are a poor substitute for enum types • Simply use enum types directly (See Bloch Item 30) • Strings are poor substitutes for aggregate types // Innappropriate use of String as aggregate type String compounKey = classname + “#” + i.next(); • What if delimeter “#” is in classname or i.next()? • How do you access fields (except by parsing)? • What about equals(), compareTo(), and toString()? • Better to simply write a class to represent the aggregate • Strings are poor substitutes for capabilities • A capability grants access to a resource • The problem is that the String namespace is global • Hence, anyone can create any String.
Item 51: Beware the Performance of String Concatenation // Inappropriate use of string concatenation – performs horribly public String statement() { String result = “”; for (int i=0; I < numItems(); i++) { result += lineForItem(i); // String concatenation } } // StringBuilder version – much faster public String statement() { StringBuilder b = new StringBuilder(numItems * LINE_WIDTH); for (int i=0; I < numItems(); i++) { b.append( lineForItem(i)); } return b.toString(); }
Item 52: Refer to Objects by Their Interfaces // Good – uses interfaces as type List <Subscriber> subscribers = new Vector<Subscriber>(); // Bad – uses class as type Vector <Subscriber> subscribers = new Vector<Subscriber>(); // Second form prohibits maintenance change to List <Subscriber> subscribers = new ArrayList<Subscriber>(); • If you get into the habit of using interfaces as types • Your programs will be much more flexible • If appropriate interface types exist, use for • parameters • return values • variables • fields
Item 53: Prefer Interfaces to Reflection • Reflection allows full access to any class • Possible to obtain, all Constructors, Methods, and Fields • Update or invoke; eg Method.invoke • Powerful mechanism! But there is a price: • You lose all the benefits of compile time checking • Code for reflexive access is cumbersome and verbose • Performance suffers • As a rule, objects should not be reflexively accessed at runtime • To limit use of reflection • Create instances reflectively, but • Access instances through an interface or superclass • You may not even have to use java.lang.reflect
More Item 53 // Reflective instantiation with interface access // Bulky (vs calling a constructor), with possible runtime errors public static void main (String [] args) { // Translate the class name into a Class object Class<?> cl = null; // note that try/catch block interrupts declaration try{ cl = Class.forName (args[0]); } catch (ClassNotFoundException e) { // runtime, not compile time, error System.err.println(”Class not found.”); System.exit(1); // terminates JVM! – generally bad practice // ok for command line utility } // Instantiate the class Set <String> s = null; try{ s = (Set<String>) cl.newInstance(); } catch (IllegalAccessException e) { // runtime, not compile time, error System.err.println(”Class not accessible.”); System.exit(1); } catch (InstantiationException e) { // runtime, not compile time, error System.err.println(”Class not instantiable.”); System.exit(1); } // Exercise the set s.addAll(Arrays.asList(args).subList(1, args.length)); System.out.println(s); }
Item 54: Use Native Methods Judiciously • Java allows calls to code written in other languages • Three historical reasons • Access to platform specific facilities • Access to legacy code • Performance critical sections • First and third reasons less compelling now • Java releases now features access to platform specific facilities • It is rarely advisable to use native methods for performance • Calls to native methods are now often slower • JVMs are much more efficient
Item 55: Optimize Judiciously • Three quotes • More computing sins are committed in the name of efficiency (without necessarily achieving it) than for any other single reason – including blind stupidity (William A. Wulf, 1972) • We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil (Donald E. Knuth, 1974) • We follow two rules in the matter of optimization (M.A. Jackson, 1975) Rule 1: Don’t do it. Rule 2: (for experts only) Don’t do it yet – that is, not until you have a perfectly clear and unoptimized solution • Note that this advice is 3+ decades old • Think how slow/limited computer systems were in the 1970s • Bottom line: Advice is even more relevant now
More Item 55 • Strive to write good programs rather than fast ones • But avoid design decisions that limit performance • Consider effect of API decisions, wire-level protocols, and data formats • Example: java.awt.Component class • public Dimension getSize() // return component size • Return type is a mutable height/width record • No sharing possible, so, a new object created on every call • Ideally, Dimension should be immutable • Too late now! • As of release 1.2, two new methods added: • int getHeight(), int getWidth() • Unfortunately, doesn’t help legacy code
More Item 55 • Fortunate fact • Good API design usually consistent with good performance • Don’t warp an API for performance reasons • Performance problem may go away in future releases • But API design is permanent • If you have to optimize (for experts only!) • Measure performance before and after optimization • Use a profiling tool • Results often conflict with intuition • Performance problems are “needles in haystacks” • In a big haystack, you need a metal detector • In a big program, you need hard data • Java resists easy definition of costs for primitive operations • Performance varies from JVM to JVM
Item 56: Adhere to Generally Accepted Naming Conventions • Packages • com.google.inject, org.joda.time.format • Class and Interface names • Timer, FutureTask, LinkedHashMap, HttpServlet • Method and Field names • Remove(), ensureCapacity(), getCrc() • Local Variable • i, xref, houseNumber • Constant • MIN_VALUE, NEGATIVE_INFINITY • Type Parameter • T, E, K, V, X, T1, T2
More Item 56: • Methods that perform some actions • Verb or verb phrase • append(), drawImage() • Methods that return boolean • Name usually starts with “is”; sometimes “has” • isDigit(), isProbablePrime(), isEmpty(), isEnabled(), hasSiblings() • Methods that return nonboolean • noun, noun phrase, or verb phrase starting with “get” • size(), hashCode(), getTime() • “get” form required for Beans; other form often more readable • “getters” usually have “setters” (unless immutable…) • Special cases • type conversion methods use “to” • toString(), toArray() • view methods use “as” • asType(), asList() • Common static factory names • valueOf(), of(), getInstance(), newInstance(), getType(), and newType()