Java Puzzle 9: Tweet - Solution
Feb 10, 2015Here's the puzzle again:
Java puzzle, write A to win: class B extends A{B(Long i){new B(i/Long.compare(i,i));System.out.println("Win");}} http://t.co/dabnwJIlnP
— Wouter Coekaerts (@WouterCoekaerts) February 2, 2015
The challenge is that you have to reach a statement that comes after an infinitely recursing constructor call, that also tries to divide by zero. You need to avoid a StackOverflowError
and an ArithmeticException
to get there.
Avoiding infinite recursion
How do we avoid the infinite recursion of B
's constructor from calling itself? Looking at that syntax, there's no doubt that's a constructor call. So to avoid recursion it must be a call to a constructor of a different class. All we control is the super class; so what if there's another class called B
in there? Let's try that with a simpler example:
This prints A$B
! When resolving a class name, the compiler surprisingly looks at inner classes of a superclasses before it considers using the current class. One down.
Avoiding division by zero
Next, how do we avoid that i/Long.compare(i,i)
throws an ArithmeticException
?
You might try the same trick, creating another class A.Long
. But that doesn't work out here. The constructor of B
takes Long i
as argument. If that's not java.lang.Long
, then i/something
doesn't compile. You cannot divide an instance of A.B
by something; it cannot be unboxed into a long
.
compare(i,i)
on the java.lang.Long
class will always return zero, leading to the division by zero. So we want that to resolve to a different method. The catch is that types (as in Long i
) are not resolved in the same way as method calls (as in Long.compare
). If we have a field called Long, then Long.compare
is a method call on that. Simpler demonstration:
That prints class java.lang.Long
in the first line because it's looking for a class, and then class A
because when resolving a method the A.Long
field hides the java.lang.Long
class.
Putting it all together
Applying that to the original puzzle, there is one more minor obstacle to overcome. The main method in our class A
needs to call the constructor of B
without getting distracted by A.B
. Just doing new B()
would call the wrong constructor. Usually you would solve such ambiguity with a fully qualified name; but since we're in the default package, B
is the fully qualified name already. A simple indirection through another class, called S
here, solves that. Giving the full solution:
For bonus points, if you're into code golf and want to fit that solution in a tweet with room for more: a static initializer is less characters than a main method, and a lambda saves some chars too. It also throws an exception about not finding the main method but that's only after the win.
Solution: class A{static{new S();}java.util.Comparator Long=(i,j)->1;class B{B(Long i){}}}class S{{new B(0L);}} http://t.co/DvGa517ZZl
— Wouter Coekaerts (@WouterCoekaerts) February 10, 2015
As usual, submitted solutions have been moved to comments of this post.
16 comments
Thanks for another fun puzzle. All the below goes in A.java as instructed, including the class C.
This took me almost 2 hours ! Fantastic puzzle !
Fun puzzle. Thanks!
Easy win:
My solution’s A.java file:
I think the class Plugh could be replaced with reflective instantiation of B using Class#forName and friends, but that seemed like more work.
class A{java.util.Comparator Long=(x,y)->1;class B{B(long y){}}public static void main(String[]x){new X();}}class X
A very neat puzzle! Cleverly put together as well.
My solution:
First of all, thanks Wouter! Below my solution:
Second try, now with a main method: