Java Code Talk, Part 5
Josh: Last week's first puzzler was to figure out what's wrong with this program:
import java.util.*; public class Name { public static void main(String[] args) { Set s = new HashSet(); s.add(new Name("Donald", "Duck")); System.out.println(s.contains(new Name("Donald", "Duck"))); } private String first, last; public Name(String first, String last) { if (first == null || last == null) throw new NullPointerException(); this.first = first; this.last = last; } public boolean equals(Name other) { return first.equals(other.first) && last.equals(other.last); } public int hashCode() { return 31 * first.hashCode() + last.hashCode(); } }
Neal: If you attended our JavaOneSM Puzzlers talk, or read my brother's book, you know that classes that override equals have to override hashCode too or they won't work. This class does override hashCode, but it still doesn't work. You'd expect the program to print out true, but it prints out false. What's the deal?
Josh: Well, the program does override hashCode, but it doesn't override equals.
Neal: Of course it does; I see it with my own four eyes.
Josh: No, what you see is overloading, not overriding. If you look closely, you'll see that the declaration for equals has an argument of type Name, not Object. It isn't overriding unless the argument types match exactly. When HashSet invokes the equals method on a Name, it gets the implementation inherited from Object. Of course you know what that implementation does: two object references are only considered equal if they refer to the same object. In this case, you have two unequal objects, both of which think they're Donald Duck.
Neal: Got it. Also, the overriding hashCode method doesn't match the inherited equals method, which violates the hashCode contract.
Josh: All in all, a proper mess. But it's easy to fix. Just supplement the "self-typed" equals method with one that takes an Object:
public boolean equals(Object o) { return o instanceof Name && equals((Name)o); }
Neal: Brilliant. Last week's second puzzler was to figure out what this program prints (and why):
public class Search { static int[] specialNums = { 1, 5, 10, 15, 37, 102, 776, 12 }; static boolean isSpecial(int n) { try { for (int i=0; i < specialNums.length; i++) if (specialNums[i] == n) return true; } finally { return false; } } public static void main(String[] args) { System.out.println(isSpecial(16)); System.out.println(isSpecial(12)); } }
If you attended our JavaOne talk, you probably remember that the finally block will be executed before the try statement completes. In this case, the finally block interrupts the attempt to return true in the search. The isSpecial method always returns false, no matter what. So the program prints:
false false
Fixing it is as easy as eliminating the try-finally statement (but leaving the enclosed for loop and return statement).
Josh: In this the final installment of our column, we'll each leave you with one puzzler to take home. First, what does this program print, and why? (If you think you know, run it just to make sure.)
import java.util.*; public class RandomSet { public static void main(String[] args) { Set s = new HashSet(); for (int i=0; i<100; i++) s.add(new Integer(new Random().nextInt())); System.out.println(s.size()); } }
Neal: My final puzzler is very hard because it combines a number of subtle language questions into a single program. Don't assume your compiler will give the right answer. What should this program print, and why?
class Twisted { private final String name; private void prname() { System.out.println(name); } Twisted(String name) { this.name = name; } public static void main(String[] args) { new Twisted("main").doit(); } private void doit() { new Twisted("doit") { void f() { prname(); } }.f(); } }
Here's a hint: Pour yourself a stiff drink and cuddle up with your well-thumbed copy of The Java(TM) Language Specification, Second Edition. Read sections 6.5.6.1, 8.2.1.3, and 15.9.1. Now read them again. When you wake up, read them one last time and all will become clear.
Josh: Well, I have no idea what that program does, but the lesson is clear: if you can't easily tell what a program is supposed to do, it probably doesn't do what you want it to.
Neal: Right. Also, be careful when using nested classes. And last but not least, don't code like my brother.
Josh: Don't code like my brother. And send your puzzlers to javapuzzlers@sun.com. If we use yours, we'll give you credit. In fact, you might just be the lucky winner of a prototype JavaRock™©® Reg. Penna. Dept. Agr.