280 likes | 285 Views
Learn effective Java programming techniques on minimizing variable scope, using loops efficiently, and implementing and utilizing the Iterable interface. Get insights from the book "Effective Java" by Joshua Bloch.
E N D
Effective Java: General Programming Spring 2013
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()