SlideShare ist ein Scribd-Unternehmen logo
1 von 63
Java Puzzlers
Scraping the Bottom of The Barrel
Joshua Bloch and Jeremy Manson
May 10, 2011
Some people say seeing is believing...
4   Fraser 1908
5   Fraser 1908
6   Fraser 1908
7   Logvinenko 1999
8   Logvinenko 1999
9   Logvinenko 1999
10   Kitaoka 1998
11   Kitaoka 1998
12   Kitaoka 1998
And Now, in Code!

• Six NEW Java programming language puzzles
     – Short program with curious behavior
     – What does it print? (multiple choice)
     – The mystery revealed
     – How to fix the problem
     – The moral




13
1. “Time for a Change”

If you pay $2.00 for a gasket that costs
$1.10, how much change do you get?



public class Change {
    public static void main(String args[]) {
        System.out.println(2.00 - 1.10);
    }
}




14
1. “A Change is Gonna Come”

If you pay $2.00 for a gasket that costs
$1.10, how much change do you get?



import java.math.BigDecimal;

public class Change {
    public static void main(String args[]) {
        BigDecimal payment = new BigDecimal(2.00);
        BigDecimal cost = new BigDecimal(1.10);
        System.out.println(payment.subtract(cost));
    }
}

15
What Does It Print?

(a) 0.9
(b) 0.90
(c) 0.8999999999999999
(d) None of the above


import java.math.BigDecimal;

public class Change {
    public static void main(String args[]) {
        BigDecimal payment = new BigDecimal(2.00);
        BigDecimal cost = new BigDecimal(1.10);
        System.out.println(payment.subtract(cost));
    }
}

16
What Does It Print?

(a) 0.9
(b) 0.90
(c) 0.8999999999999999
(d) None of the above
    0.899999999999999911182158029987476766109466552734375




We used the wrong BigDecimal constructor

17
Another Look

The specification says this:
 public BigDecimal(double val)
  Translates a double into a BigDecimal with the exact decimal
  representation of the double's binary floating-point value.


import java.math.BigDecimal;

public class Change {
    public static void main(String args[]) {
        BigDecimal payment = new BigDecimal(2.00);
        BigDecimal cost = new BigDecimal(1.10);
        System.out.println(payment.subtract(cost));
    }
}

18
How Do You Fix It?


          Prints 0.90




import java.math.BigDecimal;

public class Change {
    public static void main(String args[]) {
        BigDecimal payment = new BigDecimal("2.00");
        BigDecimal cost = new BigDecimal("1.10");
        System.out.println(payment.subtract(cost));
    }
}

19
The Moral

• Avoid float and double where exact answers are required
     – For example, when dealing with money
• Use BigDecimal, int, or long instead
• Use new BigDecimal(String), not new BigDecimal(double)
• BigDecimal.valueOf(double) is better, but not perfect;
  use it for non-constant values.
• For API designers
     – Make it easy to do the commonly correct thing
     – Make it possible to do exotic things




20
2. “Size Matters”

public class Size {
    private enum Sex { MALE, FEMALE }

     public static void main(String[] args) {
         System.out.print(size(new HashMap<Sex, Sex>()) + " ");
         System.out.print(size(new EnumMap<Sex, Sex>(Sex.class)));
     }

     private static int size(Map<Sex, Sex> map) {
         map.put(Sex.MALE,   Sex.FEMALE);
         map.put(Sex.FEMALE, Sex.MALE);
         map.put(Sex.MALE,   Sex.MALE);
         map.put(Sex.FEMALE, Sex.FEMALE);
         Set<Map.Entry<Sex, Sex>> set =
                 new HashSet<Map.Entry<Sex, Sex>>(map.entrySet());
         return set.size();
     }
}                                    Thanks to Chris Dennis, via Alex Miller

21
What Does It Print?                                       (a) 2 1
                                                          (b) 2 2
public class Size {                                       (c) 4 4
    private enum Sex { MALE, FEMALE }
                                                          (d) None of the above
     public static void main(String[] args) {
         System.out.print(size(new HashMap<Sex, Sex>()) + " ");
         System.out.print(size(new EnumMap<Sex, Sex>(Sex.class)));
     }

     private static int size(Map<Sex, Sex> map) {
         map.put(Sex.MALE,   Sex.FEMALE);
         map.put(Sex.FEMALE, Sex.MALE);
         map.put(Sex.MALE,   Sex.MALE);
         map.put(Sex.FEMALE, Sex.FEMALE);
         Set<Map.Entry<Sex, Sex>> set =
                 new HashSet<Map.Entry<Sex, Sex>>(map.entrySet());
         return set.size();
     }
}

22
What Does It Print?

(a) 2 1
(b) 2 2
(c) 4 4
(d) None of the above




Enumerating over entry sets works better for some Map implementations than others

23
Another Look
 EnumMap entrySet iterator repeatedly returns the same Entry :(
public class Size {
    private enum Sex { MALE, FEMALE }

     public static void main(String[] args) {
         System.out.print(size(new HashMap<Sex, Sex>()) + " ");
         System.out.print(size(new EnumMap<Sex, Sex>(Sex.class)));
     }

     private static int size(Map<Sex, Sex> map) {
         map.put(Sex.MALE,   Sex.FEMALE);
         map.put(Sex.FEMALE, Sex.MALE);
         map.put(Sex.MALE,   Sex.MALE);
         map.put(Sex.FEMALE, Sex.FEMALE);
         Set<Map.Entry<Sex, Sex>> set =
                 new HashSet<Map.Entry<Sex, Sex>>(map.entrySet());
         return set.size();
     }
}

24
WTF? Is this a bug?

• Perhaps, but it’s been around since 1997
     – Josh thought it was legal when he designed Collections
     – But the spec is, at best, ambiguous on this point
• Several Map implementations did this
     – IdentityHashMap, ConcurrentHashMap, EnumMap
     – ConcurrentHashMap was fixed a long time ago
• Happily, Android did not perpetuate this behavior




25
How Do You Fix It?
 Copy the entries and insert manually
                                                         Prints 2 2
public class Size {
    private enum Sex { MALE, FEMALE }

     public static void main(String[] args) {
         System.out.print(size(new HashMap<Sex, Sex>()) + " ");
         System.out.print(size(new EnumMap<Sex, Sex>(Sex.class)));
     }

     private static int size(Map<Sex, Sex> map) {
         map.put(Sex.MALE,   Sex.FEMALE);
         map.put(Sex.FEMALE, Sex.MALE);
         map.put(Sex.MALE,   Sex.MALE);
         map.put(Sex.FEMALE, Sex.FEMALE);
         Set<Map.Entry<Sex, Sex>> set = new HashSet<Map.Entry<Sex, Sex>>();
         for (Map.Entry<Sex, Sex> e : map.entrySet())
             set.add(new AbstractMap.SimpleImmutableEntry<Sex, Sex>(e));
         return set.size();
     }
}
26
The Moral

• Iterating over entrySet requires care
     – Entry may become invalid when Iterator advances
     – EnumMap and IdentityHashMap are the only JDK 6 Map implementations with this behavior
       • No Android Map implementations have this behavior
     – new HashSet<EntryType>(map.entrySet()) idiom fails in the face of this behavior
• For API designers
     – Don’t violate the principle of least astonishment
     – Don’t worsen your API to improve performance (unless you have no choice)
       • It may seem like a good idea at the time, but you’ll live to regret it




27
3. “The Match Game”

import java.util.regex.*;

public class Match {
    public static void main(String[] args) {
        Pattern p = Pattern.compile("(aa|aab?)+");
        int count = 0;
        for(String s = ""; s.length() < 200; s += "a")
            if (p.matcher(s).matches())
                count++;
        System.out.println(count);
    }
}




28
What Does It Print?

import java.util.regex.*;

public class Match {
    public static void main(String[] args) {
        Pattern p = Pattern.compile("(aa|aab?)+");
        int count = 0;
        for(String s = ""; s.length() < 200; s += "a")
            if (p.matcher(s).matches())
                count++;
        System.out.println(count);
    }
}
                                         (a) 99
                                         (b) 100
                                         (c) Throws an exception
                                         (d) None of the above

29
What Does It Print?

(a) 99
(b) 100
(c) Throws an exception
(d) None of the above: runs for >1015 years before printing anything; the Sun won’t
    last that long




The regular expression exhibits catastrophic backtracking

30
What is Catastrophic Backtracking?
 Here’s how matcher tests "aaaaa" for "(aa|aab?)+"
                                                     aa     fail

                                                     aab?
                                            aa              fail


                                           aab?
                                                      aa    fail
                                aa
                                                     aab?
                                                            fail


                                                     aa     fail
                               aab?
                                           aa        aab?
                                                            fail

                                           aab?
                                                            fail
                                                     aa
                                                     aab?
                                                            fail
This is exponential in length of string!   O(2n/2)
31
How Do You Fix It?

import java.util.regex.*;

public class Match {
    public static void main(String[] args) {                 Prints 99
        Pattern p = Pattern.compile("(aab?)+");
        int count = 0;
        for(String s = ""; s.length() < 200; s += "a")
            if (p.matcher(s).matches())
                count++;
        System.out.println(count);
    }
}




The regex "(aab?)+" matches exactly the same strings as
"(aa|aab?)+" but doesn’t exhibit catastrophic backtracking
32
The Moral

• To avoid catastrophic backtracking, ensure there’s only one way to match
  each string
• This goes way beyond Java
     – Affects most regular expression systems
     – Enables denial-of-service attacks
• Since regexes provide grouping information, they can’t just be compiled into
  optimal DFAs
     – Matcher must try all combinations of subpatterns
• Just because you can express it concisely doesn’t mean computation is fast




33
4. “That Sinking Feeling”

abstract class Sink<T> {
    abstract void add(T... elements);
    void addUnlessNull(T... elements) {
       for (T element : elements)
          if (element != null)
              add(element);
    }
}

public class StringSink extends Sink<String> {
    private final List<String> list = new ArrayList<String>();
    void add(String... elements) {
        list.addAll(Arrays.asList(elements));
    }
    public String toString() { return list.toString(); }
    public static void main(String[] args) {
        Sink<String> ss = new StringSink();
        ss.addUnlessNull("null", null);
        System.out.println(ss);
    }
}
34
What Does It Print?

abstract class Sink<T> {
    abstract void add(T... elements);
                                             (a)   [null]
    void addUnlessNull(T... elements) {      (b)   [null, null]
       for (T element : elements)
          if (element != null)
                                             (c)   NullPointerException
              add(element);                  (d)   None of the above
    }
}

public class StringSink extends Sink<String> {
    private final List<String> list = new ArrayList<String>();
    void add(String... elements) {
        list.addAll(Arrays.asList(elements));
    }
    public String toString() { return list.toString(); }
    public static void main(String[] args) {
        Sink<String> ss = new StringSink();
        ss.addUnlessNull("null", null);
        System.out.println(ss);
    }
}
35
What Does It Print?

(a) [null]
(b) [null, null]
(c) NullPointerException
(d) None of the above: ClassCastException




Varargs and generics don’t get along very well.

36
Another Look
Stack trace is very confusing!
abstract class Sink<T> {
    abstract void add(T... elements);
    void addUnlessNull(T... elements) {
       for (T element : elements)
          if (element != null)
              add(element);                    // (2)
    }
}

public class StringSink extends Sink<String> { // (3) Throws ClassCastException!
    private final List<String> list = new ArrayList<String>();
    void add(String... elements) {
        list.addAll(Arrays.asList(elements));
    }
    public String toString() { return list.toString(); }
    public static void main(String[] args) {
        Sink<String> ss = new StringSink();
        ss.addUnlessNull("null", null);         // (1)
        System.out.println(ss);
    }
}
37
What’s Going On Under the Covers?
Varargs, erasure, and a bridge method :(
abstract class Sink<T> {
    abstract void add(T... elements);    // Really Object[]
    void addUnlessNull(T... elements) { // Really Object[]
       for (T element : elements)
          if (element != null)
              add(element); // Creates Object[]
    }
}
public class StringSink extends Sink<String> {
    private final List<String> list = new ArrayList<String>();
    /** Synthetic bridge method – not present in source! */
    void add(Object[] a) { // Overrides abstract method
        add((String[]) a); // Really throws ClassCastException!
    }
    void add(String... elements) { list.addAll(Arrays.asList(elements)); }
    public String toString() { return list.toString(); }
    public static void main(String[] args) {
        Sink<String> ss = new StringSink();
        ss.addUnlessNull("null", null); // Creates String[]
        System.out.println(ss);
    }
}
38
The Compiler Tried to Warn You!

javac StringSink.java
Note: StringSink.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

And hopefully:
javac -Xlint:unchecked StringSink.java
StringSink.java:8: warning: [unchecked] unchecked generic array creation of
type T[] for varargs parameter
              add(element);
                 ^




39
How Do You Fix It?
Replace varargs and arrays with collections
abstract class Sink<T> {
    abstract void add(Collection<T> elements);
    void addUnlessNull(Collection<T> elements) {
       for (T element : elements)
          if (element != null)
                                                            Prints [null]
              add(Collections.singleton(element));
    }
}

public class StringSink extends Sink<String> {
    private final List<String> list = new ArrayList<String>();
    void add(Collection<String> elements) {
        list.addAll(elements); // No Arrays.asList
    }
    public String toString() { return list.toString(); }
    public static void main(String[] args) {
        Sink<String> ss = new StringSink();
        ss.addUnlessNull(Arrays.asList("null", null));
        System.out.println(ss);
    }
}
40
The Moral

• Varargs provide a leaky abstraction
     – Arrays leak through the veneer of varargs
• Generics and arrays don’t get along well
     – So generics and varargs don’t get along well
• Prefer collections to arrays
     – Especially in APIs
• Don't ignore compiler warnings
     – Ideally, eliminate them by fixing the code. If that’s not possible:
        • Prove that there’s no real problem and write down your proof in a comment
        • Disable the warning locally with a @SuppressWarnings annotation



41
5. “Glommer Pile”

public class Glommer<T> {
    String glom(Collection<?> objs) {
        String result = "";
        for (Object o : objs)
            result += o;
        return result;
    }

     int glom(List<Integer> ints) {
         int result = 0;
         for (int i : ints)
             result += i;
         return result;
     }
     public static void main(String args[]) {
         List<String> strings = Arrays.asList("1", "2", "3");
         System.out.println(new Glommer().glom(strings));
     }
}


42
What Does It Print?

public class Glommer<T> {                               (a) 6
    String glom(Collection<?> objs) {
        String result = "";
                                                        (b) 123
        for (Object o : objs)                           (c) Throws an exception
            result += o;
        return result;                                  (d) None of the above
    }

     int glom(List<Integer> ints) {
         int result = 0;
         for (int i : ints)
             result += i;
         return result;
     }
     public static void main(String args[]) {
         List<String> strings = Arrays.asList("1", "2", "3");
         System.out.println(new Glommer().glom(strings));
     }
}


43
What Does It Print?

(a) 6
(b) 123
(c) Throws an exception: ClassCastException
(d) None of the above




Raw types are dangerous

44
Another Look
Raw type discards all generic type information, causing incorrect overload resolution
public class Glommer<T> {
    String glom(Collection<?> objs) {
        String result = "";
        for (Object o : objs)
            result += o;
        return result;
    }

     int glom(List<Integer> ints) {
         int result = 0;
         for (int i : ints)
             result += i;
         return result;
     }
     public static void main(String args[]) {
         List<String> strings = Arrays.asList("1", "2", "3");
         System.out.println(new Glommer().glom(strings));
     }
}


45
The Compiler Tried to Warn You!

javac Glommer.java
Note: Glommer.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

And hopefully:

javac -Xlint:unchecked Glommer.java
Glommer.java:20: warning: [unchecked] unchecked call to
glom(List<java.lang.Integer>) as a member of the raw type Glommer
        System.out.println(new Glommer().glom(strings));
                                             ^




46
You Could Fix it Like This...
Specify a type parameter for Glommer
public class Glommer<T> {
    String glom(Collection<?> objs) {
        String result = "";
        for (Object o : objs)
            result += o;                            Prints 123
        return result;
    }

     int glom(List<Integer> ints) {
         int result = 0;
         for (int i : ints)
             result += i;
         return result;
     }
     public static void main(String args[]) {
         List<String> strings = Arrays.asList("1", "2", "3");
         System.out.println(new Glommer<Random>().glom(strings));
     }
}


47
Or you Could Fix it Like This...
Remove extraneous type parameter from Glommer
public class Glommer { // Look Ma, no type parameter!
    String glom(Collection<?> objs) {
        String result = "";
        for (Object o : objs)
            result += o;                           Prints   123
        return result;
    }

     int glom(List<Integer> ints) {
         int result = 0;
         for (int i : ints)
             result += i;
         return result;
     }
     public static void main(String args[]) {
         List<String> strings = Arrays.asList("1", "2", "3");
         System.out.println(new Glommer().glom(strings));
     }
}


48
But This is even better
Make Glommer a static utility class
public class Glommer {
    static String glom(Collection<?> objs) {
        String result = "";
        for (Object o : objs)
            result += o;                           Prints 123
        return result;
    }

     static int glom(List<Integer> ints) {
         int result = 0;
         for (int i : ints)
             result += i;
         return result;
     }
     public static void main(String args[]) {
         List<String> strings = Arrays.asList("1", "2", "3");
         System.out.println(glom(strings)); // No Glommer instance!
     }
}


49
The Moral

• Never use raw types in new code!
• Raw types lose all generic type information
     – Can break overload resolution
• Do not ignore compiler warnings, even when they’re indecipherable
     – Unchecked warnings mean automatically generated casts can fail at runtime




50
6. “It’s Elementary” (2004; 2010 remix)

public class Elementary {
    public static void main(String[] args) {
        System.out.print(12345 + 5432l);
        System.out.print(" ");
        System.out.print(01234 + 43210);
    }
}




51
What Does It Print?

public class Elementary {
    public static void main(String[] args) {
        System.out.print(12345 + 5432l);
        System.out.print(" ");
        System.out.print(01234 + 43210);
    }
}
                                       (a) 17777   44444
                                       (b) 17777   43878
                                       (c) 66666   44444
                                       (d) 66666   43878



52
What Does It Print?

(a) 17777 44444
(b) 17777 43878
(c) 66666 44444
(d) 66666 43878




Program doesn’t say what you think it does!
Also, leading zeros can cause trouble.

53
Another Look

public class Elementary {
    public static void main(String[] args) {
         System.out.print(12345 + 5432l);
         System.out.print(" ");
         System.out.print(01234 + 43210);
     }
}




1 - the digit one
l - the lowercase letter el
54
Another Look, Continued

public class Elementary {
    public static void main(String[] args) {
         System.out.print(12345 + 5432l);
         System.out.print(" ");
         System.out.print(01234 + 43210);
     }
}




01234 is an octal literal equal to 1,2348, or 668

55
How Do You Fix It?

public class Elementary {
    public static void main(String[] args) {
        System.out.print(12345 + 54321); // The digit 1
        System.out.print(" ");
        System.out.print( 1234 + 43210); // No leading 0
    }
}


                                         Prints 66666 44444




56
The Moral

• Always use uppercase el (L) for long literals
     – Lowercase el makes the code unreadable
     – 5432L is clearly a long, 5432l is misleading

• Never use lowercase el (l) as a variable name
     – Not this: List<String> l = ... ;
     – But this: List<String> list = ...;
• Never precede an int literal with 0 unless you actually want to express it in octal
     – When you use an octal literal, always add a comment expressing your intent




57
A Summary of the Traps

1. Use new BigDecimal(String), not new BigDecimal(double)
2. Don’t assume that Map.Entry objects in entrySet iteration are stable
     new HashSet<EntryType>(map.entrySet()) idiom fails for EnumMap, IdentityHashMap
3. Beware of catastrophic backtracking when writing regular expressions
4. Generics and arrays don’t mix–don’t ignore compiler warnings!
5. Never use raw types in new code–they lose all generic type information
6. Always use uppercase L for long literals; never use 0 to pad int literal




58
Lessons for API Designers

• Make it easy to do the commonly correct thing; possible to do exotic things
• Don’t violate the principle of least astonishment
• Don’t worsen your API to improve performance (unless you have no choice)




59
Conclusion

• Java platform is reasonably simple and elegant
     – But it has a few sharp corners—avoid them!
• Keep programs clear and simple
• If you aren’t sure what a program does, it probably doesn’t do what you want
• Use FindBugs and a good IDE
• Don’t code like my brother




60
Shameless Commerce Division

                              • 95 Puzzles
                              • 52 Illusions
                              • Tons of fun




61
Java Puzzlers
Scraping the Bottom of The Barrel

  Compliments, Marriage Proposals, Hot Tips:
     Hashtags: #io2011 #JavaPuzzlers
     Web:      http://goo.gl/sV0bg
  Complaints:   /dev/null
Java puzzlers scraping_the_bottom_of_the_barrel

Weitere ähnliche Inhalte

Was ist angesagt?

Using The Google Collections Library For Java
Using The Google Collections Library For JavaUsing The Google Collections Library For Java
Using The Google Collections Library For Java
GoogleTecTalks
 
peRm R group. Review of packages for r for market data downloading and analysis
peRm R group. Review of packages for r for market data downloading and analysispeRm R group. Review of packages for r for market data downloading and analysis
peRm R group. Review of packages for r for market data downloading and analysis
Vyacheslav Arbuzov
 
A walk in graph databases v1.0
A walk in graph databases v1.0A walk in graph databases v1.0
A walk in graph databases v1.0
Pierre De Wilde
 

Was ist angesagt? (8)

Using The Google Collections Library For Java
Using The Google Collections Library For JavaUsing The Google Collections Library For Java
Using The Google Collections Library For Java
 
JDays Lviv 2014: Java8 vs Scala: Difference points & innovation stream
JDays Lviv 2014:  Java8 vs Scala:  Difference points & innovation streamJDays Lviv 2014:  Java8 vs Scala:  Difference points & innovation stream
JDays Lviv 2014: Java8 vs Scala: Difference points & innovation stream
 
Hive function-cheat-sheet
Hive function-cheat-sheetHive function-cheat-sheet
Hive function-cheat-sheet
 
peRm R group. Review of packages for r for market data downloading and analysis
peRm R group. Review of packages for r for market data downloading and analysispeRm R group. Review of packages for r for market data downloading and analysis
peRm R group. Review of packages for r for market data downloading and analysis
 
Beauty and the beast - Haskell on JVM
Beauty and the beast  - Haskell on JVMBeauty and the beast  - Haskell on JVM
Beauty and the beast - Haskell on JVM
 
Scala vs java 8
Scala vs java 8Scala vs java 8
Scala vs java 8
 
A walk in graph databases v1.0
A walk in graph databases v1.0A walk in graph databases v1.0
A walk in graph databases v1.0
 
Clojure: The Art of Abstraction
Clojure: The Art of AbstractionClojure: The Art of Abstraction
Clojure: The Art of Abstraction
 

Andere mochten auch (6)

Java 1.5 - whats new and modern patterns (2007)
Java 1.5 - whats new and modern patterns (2007)Java 1.5 - whats new and modern patterns (2007)
Java 1.5 - whats new and modern patterns (2007)
 
Java Concurrency Gotchas
Java Concurrency GotchasJava Concurrency Gotchas
Java Concurrency Gotchas
 
Effective Java - Still Effective After All These Years
Effective Java - Still Effective After All These YearsEffective Java - Still Effective After All These Years
Effective Java - Still Effective After All These Years
 
Effective java - concurrency
Effective java - concurrencyEffective java - concurrency
Effective java - concurrency
 
Beginning Object-Oriented JavaScript
Beginning Object-Oriented JavaScriptBeginning Object-Oriented JavaScript
Beginning Object-Oriented JavaScript
 
Pollution
PollutionPollution
Pollution
 

Ähnlich wie Java puzzlers scraping_the_bottom_of_the_barrel

ภาษา C โปรแกรมย่อยและฟังก์ชันมาตรฐาน
ภาษา C โปรแกรมย่อยและฟังก์ชันมาตรฐานภาษา C โปรแกรมย่อยและฟังก์ชันมาตรฐาน
ภาษา C โปรแกรมย่อยและฟังก์ชันมาตรฐาน
Noppanon YourJust'one
 
Java puzzle-1195101951317606-3
Java puzzle-1195101951317606-3Java puzzle-1195101951317606-3
Java puzzle-1195101951317606-3
rsmuralirs
 

Ähnlich wie Java puzzlers scraping_the_bottom_of_the_barrel (20)

Scala @ TomTom
Scala @ TomTomScala @ TomTom
Scala @ TomTom
 
Java 8 Puzzlers [as presented at OSCON 2016]
Java 8 Puzzlers [as presented at  OSCON 2016]Java 8 Puzzlers [as presented at  OSCON 2016]
Java 8 Puzzlers [as presented at OSCON 2016]
 
Streams: The Good, The Bad And The Ugly
Streams: The Good, The Bad And The UglyStreams: The Good, The Bad And The Ugly
Streams: The Good, The Bad And The Ugly
 
ภาษา C โปรแกรมย่อยและฟังก์ชันมาตรฐาน
ภาษา C โปรแกรมย่อยและฟังก์ชันมาตรฐานภาษา C โปรแกรมย่อยและฟังก์ชันมาตรฐาน
ภาษา C โปรแกรมย่อยและฟังก์ชันมาตรฐาน
 
MATLAB Questions and Answers.pdf
MATLAB Questions and Answers.pdfMATLAB Questions and Answers.pdf
MATLAB Questions and Answers.pdf
 
Scala vs Ruby
Scala vs RubyScala vs Ruby
Scala vs Ruby
 
Java puzzle-1195101951317606-3
Java puzzle-1195101951317606-3Java puzzle-1195101951317606-3
Java puzzle-1195101951317606-3
 
Stata cheat sheet: data processing
Stata cheat sheet: data processingStata cheat sheet: data processing
Stata cheat sheet: data processing
 
Data import-cheatsheet
Data import-cheatsheetData import-cheatsheet
Data import-cheatsheet
 
An Intro To ES6
An Intro To ES6An Intro To ES6
An Intro To ES6
 
Real Time Big Data Management
Real Time Big Data ManagementReal Time Big Data Management
Real Time Big Data Management
 
Introduction To Lisp
Introduction To LispIntroduction To Lisp
Introduction To Lisp
 
Gate-Cs 1996
Gate-Cs 1996Gate-Cs 1996
Gate-Cs 1996
 
Java exercise1
Java exercise1Java exercise1
Java exercise1
 
Why is Java relevant? New features of Java8
Why is Java relevant? New features of Java8 Why is Java relevant? New features of Java8
Why is Java relevant? New features of Java8
 
Best Java Problems and Solutions
Best Java Problems and SolutionsBest Java Problems and Solutions
Best Java Problems and Solutions
 
FSOFT - Test Java Exam
FSOFT - Test Java ExamFSOFT - Test Java Exam
FSOFT - Test Java Exam
 
OracleCode One 2018: Java 5, 6, 7, 8, 9, 10, 11: What Did You Miss?
OracleCode One 2018: Java 5, 6, 7, 8, 9, 10, 11: What Did You Miss?OracleCode One 2018: Java 5, 6, 7, 8, 9, 10, 11: What Did You Miss?
OracleCode One 2018: Java 5, 6, 7, 8, 9, 10, 11: What Did You Miss?
 
Java Questions
Java QuestionsJava Questions
Java Questions
 
Monads and Monoids by Oleksiy Dyagilev
Monads and Monoids by Oleksiy DyagilevMonads and Monoids by Oleksiy Dyagilev
Monads and Monoids by Oleksiy Dyagilev
 

Kürzlich hochgeladen

Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Victor Rentea
 

Kürzlich hochgeladen (20)

Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
 
Platformless Horizons for Digital Adaptability
Platformless Horizons for Digital AdaptabilityPlatformless Horizons for Digital Adaptability
Platformless Horizons for Digital Adaptability
 
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdfRising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024
 
MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectors
 
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ..."I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
 
Vector Search -An Introduction in Oracle Database 23ai.pptx
Vector Search -An Introduction in Oracle Database 23ai.pptxVector Search -An Introduction in Oracle Database 23ai.pptx
Vector Search -An Introduction in Oracle Database 23ai.pptx
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
 
Mcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Mcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot ModelMcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Mcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot Model
 
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
 
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 AmsterdamDEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
 
Corporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxCorporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptx
 
Elevate Developer Efficiency & build GenAI Application with Amazon Q​
Elevate Developer Efficiency & build GenAI Application with Amazon Q​Elevate Developer Efficiency & build GenAI Application with Amazon Q​
Elevate Developer Efficiency & build GenAI Application with Amazon Q​
 
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
WSO2's API Vision: Unifying Control, Empowering Developers
WSO2's API Vision: Unifying Control, Empowering DevelopersWSO2's API Vision: Unifying Control, Empowering Developers
WSO2's API Vision: Unifying Control, Empowering Developers
 

Java puzzlers scraping_the_bottom_of_the_barrel

  • 1.
  • 2. Java Puzzlers Scraping the Bottom of The Barrel Joshua Bloch and Jeremy Manson May 10, 2011
  • 3. Some people say seeing is believing...
  • 4. 4 Fraser 1908
  • 5. 5 Fraser 1908
  • 6. 6 Fraser 1908
  • 7. 7 Logvinenko 1999
  • 8. 8 Logvinenko 1999
  • 9. 9 Logvinenko 1999
  • 10. 10 Kitaoka 1998
  • 11. 11 Kitaoka 1998
  • 12. 12 Kitaoka 1998
  • 13. And Now, in Code! • Six NEW Java programming language puzzles – Short program with curious behavior – What does it print? (multiple choice) – The mystery revealed – How to fix the problem – The moral 13
  • 14. 1. “Time for a Change” If you pay $2.00 for a gasket that costs $1.10, how much change do you get? public class Change { public static void main(String args[]) { System.out.println(2.00 - 1.10); } } 14
  • 15. 1. “A Change is Gonna Come” If you pay $2.00 for a gasket that costs $1.10, how much change do you get? import java.math.BigDecimal; public class Change { public static void main(String args[]) { BigDecimal payment = new BigDecimal(2.00); BigDecimal cost = new BigDecimal(1.10); System.out.println(payment.subtract(cost)); } } 15
  • 16. What Does It Print? (a) 0.9 (b) 0.90 (c) 0.8999999999999999 (d) None of the above import java.math.BigDecimal; public class Change { public static void main(String args[]) { BigDecimal payment = new BigDecimal(2.00); BigDecimal cost = new BigDecimal(1.10); System.out.println(payment.subtract(cost)); } } 16
  • 17. What Does It Print? (a) 0.9 (b) 0.90 (c) 0.8999999999999999 (d) None of the above 0.899999999999999911182158029987476766109466552734375 We used the wrong BigDecimal constructor 17
  • 18. Another Look The specification says this: public BigDecimal(double val) Translates a double into a BigDecimal with the exact decimal representation of the double's binary floating-point value. import java.math.BigDecimal; public class Change { public static void main(String args[]) { BigDecimal payment = new BigDecimal(2.00); BigDecimal cost = new BigDecimal(1.10); System.out.println(payment.subtract(cost)); } } 18
  • 19. How Do You Fix It? Prints 0.90 import java.math.BigDecimal; public class Change { public static void main(String args[]) { BigDecimal payment = new BigDecimal("2.00"); BigDecimal cost = new BigDecimal("1.10"); System.out.println(payment.subtract(cost)); } } 19
  • 20. The Moral • Avoid float and double where exact answers are required – For example, when dealing with money • Use BigDecimal, int, or long instead • Use new BigDecimal(String), not new BigDecimal(double) • BigDecimal.valueOf(double) is better, but not perfect; use it for non-constant values. • For API designers – Make it easy to do the commonly correct thing – Make it possible to do exotic things 20
  • 21. 2. “Size Matters” public class Size { private enum Sex { MALE, FEMALE } public static void main(String[] args) { System.out.print(size(new HashMap<Sex, Sex>()) + " "); System.out.print(size(new EnumMap<Sex, Sex>(Sex.class))); } private static int size(Map<Sex, Sex> map) { map.put(Sex.MALE, Sex.FEMALE); map.put(Sex.FEMALE, Sex.MALE); map.put(Sex.MALE, Sex.MALE); map.put(Sex.FEMALE, Sex.FEMALE); Set<Map.Entry<Sex, Sex>> set = new HashSet<Map.Entry<Sex, Sex>>(map.entrySet()); return set.size(); } } Thanks to Chris Dennis, via Alex Miller 21
  • 22. What Does It Print? (a) 2 1 (b) 2 2 public class Size { (c) 4 4 private enum Sex { MALE, FEMALE } (d) None of the above public static void main(String[] args) { System.out.print(size(new HashMap<Sex, Sex>()) + " "); System.out.print(size(new EnumMap<Sex, Sex>(Sex.class))); } private static int size(Map<Sex, Sex> map) { map.put(Sex.MALE, Sex.FEMALE); map.put(Sex.FEMALE, Sex.MALE); map.put(Sex.MALE, Sex.MALE); map.put(Sex.FEMALE, Sex.FEMALE); Set<Map.Entry<Sex, Sex>> set = new HashSet<Map.Entry<Sex, Sex>>(map.entrySet()); return set.size(); } } 22
  • 23. What Does It Print? (a) 2 1 (b) 2 2 (c) 4 4 (d) None of the above Enumerating over entry sets works better for some Map implementations than others 23
  • 24. Another Look EnumMap entrySet iterator repeatedly returns the same Entry :( public class Size { private enum Sex { MALE, FEMALE } public static void main(String[] args) { System.out.print(size(new HashMap<Sex, Sex>()) + " "); System.out.print(size(new EnumMap<Sex, Sex>(Sex.class))); } private static int size(Map<Sex, Sex> map) { map.put(Sex.MALE, Sex.FEMALE); map.put(Sex.FEMALE, Sex.MALE); map.put(Sex.MALE, Sex.MALE); map.put(Sex.FEMALE, Sex.FEMALE); Set<Map.Entry<Sex, Sex>> set = new HashSet<Map.Entry<Sex, Sex>>(map.entrySet()); return set.size(); } } 24
  • 25. WTF? Is this a bug? • Perhaps, but it’s been around since 1997 – Josh thought it was legal when he designed Collections – But the spec is, at best, ambiguous on this point • Several Map implementations did this – IdentityHashMap, ConcurrentHashMap, EnumMap – ConcurrentHashMap was fixed a long time ago • Happily, Android did not perpetuate this behavior 25
  • 26. How Do You Fix It? Copy the entries and insert manually Prints 2 2 public class Size { private enum Sex { MALE, FEMALE } public static void main(String[] args) { System.out.print(size(new HashMap<Sex, Sex>()) + " "); System.out.print(size(new EnumMap<Sex, Sex>(Sex.class))); } private static int size(Map<Sex, Sex> map) { map.put(Sex.MALE, Sex.FEMALE); map.put(Sex.FEMALE, Sex.MALE); map.put(Sex.MALE, Sex.MALE); map.put(Sex.FEMALE, Sex.FEMALE); Set<Map.Entry<Sex, Sex>> set = new HashSet<Map.Entry<Sex, Sex>>(); for (Map.Entry<Sex, Sex> e : map.entrySet()) set.add(new AbstractMap.SimpleImmutableEntry<Sex, Sex>(e)); return set.size(); } } 26
  • 27. The Moral • Iterating over entrySet requires care – Entry may become invalid when Iterator advances – EnumMap and IdentityHashMap are the only JDK 6 Map implementations with this behavior • No Android Map implementations have this behavior – new HashSet<EntryType>(map.entrySet()) idiom fails in the face of this behavior • For API designers – Don’t violate the principle of least astonishment – Don’t worsen your API to improve performance (unless you have no choice) • It may seem like a good idea at the time, but you’ll live to regret it 27
  • 28. 3. “The Match Game” import java.util.regex.*; public class Match { public static void main(String[] args) { Pattern p = Pattern.compile("(aa|aab?)+"); int count = 0; for(String s = ""; s.length() < 200; s += "a") if (p.matcher(s).matches()) count++; System.out.println(count); } } 28
  • 29. What Does It Print? import java.util.regex.*; public class Match { public static void main(String[] args) { Pattern p = Pattern.compile("(aa|aab?)+"); int count = 0; for(String s = ""; s.length() < 200; s += "a") if (p.matcher(s).matches()) count++; System.out.println(count); } } (a) 99 (b) 100 (c) Throws an exception (d) None of the above 29
  • 30. What Does It Print? (a) 99 (b) 100 (c) Throws an exception (d) None of the above: runs for >1015 years before printing anything; the Sun won’t last that long The regular expression exhibits catastrophic backtracking 30
  • 31. What is Catastrophic Backtracking? Here’s how matcher tests "aaaaa" for "(aa|aab?)+" aa fail aab? aa fail aab? aa fail aa aab? fail aa fail aab? aa aab? fail aab? fail aa aab? fail This is exponential in length of string! O(2n/2) 31
  • 32. How Do You Fix It? import java.util.regex.*; public class Match { public static void main(String[] args) { Prints 99 Pattern p = Pattern.compile("(aab?)+"); int count = 0; for(String s = ""; s.length() < 200; s += "a") if (p.matcher(s).matches()) count++; System.out.println(count); } } The regex "(aab?)+" matches exactly the same strings as "(aa|aab?)+" but doesn’t exhibit catastrophic backtracking 32
  • 33. The Moral • To avoid catastrophic backtracking, ensure there’s only one way to match each string • This goes way beyond Java – Affects most regular expression systems – Enables denial-of-service attacks • Since regexes provide grouping information, they can’t just be compiled into optimal DFAs – Matcher must try all combinations of subpatterns • Just because you can express it concisely doesn’t mean computation is fast 33
  • 34. 4. “That Sinking Feeling” abstract class Sink<T> { abstract void add(T... elements); void addUnlessNull(T... elements) { for (T element : elements) if (element != null) add(element); } } public class StringSink extends Sink<String> { private final List<String> list = new ArrayList<String>(); void add(String... elements) { list.addAll(Arrays.asList(elements)); } public String toString() { return list.toString(); } public static void main(String[] args) { Sink<String> ss = new StringSink(); ss.addUnlessNull("null", null); System.out.println(ss); } } 34
  • 35. What Does It Print? abstract class Sink<T> { abstract void add(T... elements); (a) [null] void addUnlessNull(T... elements) { (b) [null, null] for (T element : elements) if (element != null) (c) NullPointerException add(element); (d) None of the above } } public class StringSink extends Sink<String> { private final List<String> list = new ArrayList<String>(); void add(String... elements) { list.addAll(Arrays.asList(elements)); } public String toString() { return list.toString(); } public static void main(String[] args) { Sink<String> ss = new StringSink(); ss.addUnlessNull("null", null); System.out.println(ss); } } 35
  • 36. What Does It Print? (a) [null] (b) [null, null] (c) NullPointerException (d) None of the above: ClassCastException Varargs and generics don’t get along very well. 36
  • 37. Another Look Stack trace is very confusing! abstract class Sink<T> { abstract void add(T... elements); void addUnlessNull(T... elements) { for (T element : elements) if (element != null) add(element); // (2) } } public class StringSink extends Sink<String> { // (3) Throws ClassCastException! private final List<String> list = new ArrayList<String>(); void add(String... elements) { list.addAll(Arrays.asList(elements)); } public String toString() { return list.toString(); } public static void main(String[] args) { Sink<String> ss = new StringSink(); ss.addUnlessNull("null", null); // (1) System.out.println(ss); } } 37
  • 38. What’s Going On Under the Covers? Varargs, erasure, and a bridge method :( abstract class Sink<T> { abstract void add(T... elements); // Really Object[] void addUnlessNull(T... elements) { // Really Object[] for (T element : elements) if (element != null) add(element); // Creates Object[] } } public class StringSink extends Sink<String> { private final List<String> list = new ArrayList<String>(); /** Synthetic bridge method – not present in source! */ void add(Object[] a) { // Overrides abstract method add((String[]) a); // Really throws ClassCastException! } void add(String... elements) { list.addAll(Arrays.asList(elements)); } public String toString() { return list.toString(); } public static void main(String[] args) { Sink<String> ss = new StringSink(); ss.addUnlessNull("null", null); // Creates String[] System.out.println(ss); } } 38
  • 39. The Compiler Tried to Warn You! javac StringSink.java Note: StringSink.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details. And hopefully: javac -Xlint:unchecked StringSink.java StringSink.java:8: warning: [unchecked] unchecked generic array creation of type T[] for varargs parameter add(element); ^ 39
  • 40. How Do You Fix It? Replace varargs and arrays with collections abstract class Sink<T> { abstract void add(Collection<T> elements); void addUnlessNull(Collection<T> elements) { for (T element : elements) if (element != null) Prints [null] add(Collections.singleton(element)); } } public class StringSink extends Sink<String> { private final List<String> list = new ArrayList<String>(); void add(Collection<String> elements) { list.addAll(elements); // No Arrays.asList } public String toString() { return list.toString(); } public static void main(String[] args) { Sink<String> ss = new StringSink(); ss.addUnlessNull(Arrays.asList("null", null)); System.out.println(ss); } } 40
  • 41. The Moral • Varargs provide a leaky abstraction – Arrays leak through the veneer of varargs • Generics and arrays don’t get along well – So generics and varargs don’t get along well • Prefer collections to arrays – Especially in APIs • Don't ignore compiler warnings – Ideally, eliminate them by fixing the code. If that’s not possible: • Prove that there’s no real problem and write down your proof in a comment • Disable the warning locally with a @SuppressWarnings annotation 41
  • 42. 5. “Glommer Pile” public class Glommer<T> { String glom(Collection<?> objs) { String result = ""; for (Object o : objs) result += o; return result; } int glom(List<Integer> ints) { int result = 0; for (int i : ints) result += i; return result; } public static void main(String args[]) { List<String> strings = Arrays.asList("1", "2", "3"); System.out.println(new Glommer().glom(strings)); } } 42
  • 43. What Does It Print? public class Glommer<T> { (a) 6 String glom(Collection<?> objs) { String result = ""; (b) 123 for (Object o : objs) (c) Throws an exception result += o; return result; (d) None of the above } int glom(List<Integer> ints) { int result = 0; for (int i : ints) result += i; return result; } public static void main(String args[]) { List<String> strings = Arrays.asList("1", "2", "3"); System.out.println(new Glommer().glom(strings)); } } 43
  • 44. What Does It Print? (a) 6 (b) 123 (c) Throws an exception: ClassCastException (d) None of the above Raw types are dangerous 44
  • 45. Another Look Raw type discards all generic type information, causing incorrect overload resolution public class Glommer<T> { String glom(Collection<?> objs) { String result = ""; for (Object o : objs) result += o; return result; } int glom(List<Integer> ints) { int result = 0; for (int i : ints) result += i; return result; } public static void main(String args[]) { List<String> strings = Arrays.asList("1", "2", "3"); System.out.println(new Glommer().glom(strings)); } } 45
  • 46. The Compiler Tried to Warn You! javac Glommer.java Note: Glommer.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details. And hopefully: javac -Xlint:unchecked Glommer.java Glommer.java:20: warning: [unchecked] unchecked call to glom(List<java.lang.Integer>) as a member of the raw type Glommer System.out.println(new Glommer().glom(strings)); ^ 46
  • 47. You Could Fix it Like This... Specify a type parameter for Glommer public class Glommer<T> { String glom(Collection<?> objs) { String result = ""; for (Object o : objs) result += o; Prints 123 return result; } int glom(List<Integer> ints) { int result = 0; for (int i : ints) result += i; return result; } public static void main(String args[]) { List<String> strings = Arrays.asList("1", "2", "3"); System.out.println(new Glommer<Random>().glom(strings)); } } 47
  • 48. Or you Could Fix it Like This... Remove extraneous type parameter from Glommer public class Glommer { // Look Ma, no type parameter! String glom(Collection<?> objs) { String result = ""; for (Object o : objs) result += o; Prints 123 return result; } int glom(List<Integer> ints) { int result = 0; for (int i : ints) result += i; return result; } public static void main(String args[]) { List<String> strings = Arrays.asList("1", "2", "3"); System.out.println(new Glommer().glom(strings)); } } 48
  • 49. But This is even better Make Glommer a static utility class public class Glommer { static String glom(Collection<?> objs) { String result = ""; for (Object o : objs) result += o; Prints 123 return result; } static int glom(List<Integer> ints) { int result = 0; for (int i : ints) result += i; return result; } public static void main(String args[]) { List<String> strings = Arrays.asList("1", "2", "3"); System.out.println(glom(strings)); // No Glommer instance! } } 49
  • 50. The Moral • Never use raw types in new code! • Raw types lose all generic type information – Can break overload resolution • Do not ignore compiler warnings, even when they’re indecipherable – Unchecked warnings mean automatically generated casts can fail at runtime 50
  • 51. 6. “It’s Elementary” (2004; 2010 remix) public class Elementary { public static void main(String[] args) { System.out.print(12345 + 5432l); System.out.print(" "); System.out.print(01234 + 43210); } } 51
  • 52. What Does It Print? public class Elementary { public static void main(String[] args) { System.out.print(12345 + 5432l); System.out.print(" "); System.out.print(01234 + 43210); } } (a) 17777 44444 (b) 17777 43878 (c) 66666 44444 (d) 66666 43878 52
  • 53. What Does It Print? (a) 17777 44444 (b) 17777 43878 (c) 66666 44444 (d) 66666 43878 Program doesn’t say what you think it does! Also, leading zeros can cause trouble. 53
  • 54. Another Look public class Elementary { public static void main(String[] args) { System.out.print(12345 + 5432l); System.out.print(" "); System.out.print(01234 + 43210); } } 1 - the digit one l - the lowercase letter el 54
  • 55. Another Look, Continued public class Elementary { public static void main(String[] args) { System.out.print(12345 + 5432l); System.out.print(" "); System.out.print(01234 + 43210); } } 01234 is an octal literal equal to 1,2348, or 668 55
  • 56. How Do You Fix It? public class Elementary { public static void main(String[] args) { System.out.print(12345 + 54321); // The digit 1 System.out.print(" "); System.out.print( 1234 + 43210); // No leading 0 } } Prints 66666 44444 56
  • 57. The Moral • Always use uppercase el (L) for long literals – Lowercase el makes the code unreadable – 5432L is clearly a long, 5432l is misleading • Never use lowercase el (l) as a variable name – Not this: List<String> l = ... ; – But this: List<String> list = ...; • Never precede an int literal with 0 unless you actually want to express it in octal – When you use an octal literal, always add a comment expressing your intent 57
  • 58. A Summary of the Traps 1. Use new BigDecimal(String), not new BigDecimal(double) 2. Don’t assume that Map.Entry objects in entrySet iteration are stable new HashSet<EntryType>(map.entrySet()) idiom fails for EnumMap, IdentityHashMap 3. Beware of catastrophic backtracking when writing regular expressions 4. Generics and arrays don’t mix–don’t ignore compiler warnings! 5. Never use raw types in new code–they lose all generic type information 6. Always use uppercase L for long literals; never use 0 to pad int literal 58
  • 59. Lessons for API Designers • Make it easy to do the commonly correct thing; possible to do exotic things • Don’t violate the principle of least astonishment • Don’t worsen your API to improve performance (unless you have no choice) 59
  • 60. Conclusion • Java platform is reasonably simple and elegant – But it has a few sharp corners—avoid them! • Keep programs clear and simple • If you aren’t sure what a program does, it probably doesn’t do what you want • Use FindBugs and a good IDE • Don’t code like my brother 60
  • 61. Shameless Commerce Division • 95 Puzzles • 52 Illusions • Tons of fun 61
  • 62. Java Puzzlers Scraping the Bottom of The Barrel Compliments, Marriage Proposals, Hot Tips: Hashtags: #io2011 #JavaPuzzlers Web: http://goo.gl/sV0bg Complaints: /dev/null