Java Puzzle 7: Cookies - Solution
Aug 12, 2012Here's the solution to the cookies puzzle! As a reminder, the Count
class:
At first sight, it looks as if CookieMonster.eat
needs to return a String
with a negative length. That sounds like anti-matter. Looking closer, there are two separate requirements: the first time eat
is called, it needs to return an empty String
; and the second time, it needs to return something with a negative length()
.
The trick is, a generic method's return type can be different depending on the type context in which it is used. To demonstrate that, let's look at an example from java.util.Collections
: <T> List<T> emptyList(). In that method signature, <T>
says there is type variable T
on the method, and List<T>
is the return type. You can use this as List<String> strings = Collections.emptyList();
. That works because type inference ensures that the T
in the return type (List<T>
) adjusts to the type of the variable it is assigned to (List<String>
). When there is no context (when it is not assigned to anything) then the inferred type will be Object
(the implicit upper bound of T
). In Collections.emptyList().size()
for example emptyList()
returns a List<Object>
.
Back to our CookieMonster
, we can make eat
return anything the context expects it to return like this: static <C> C eat(String cookie)
. That's like a magic cookie. If you think 'chocolate' and put it in our mouth, it's a chocolate cookie. In Count
where it is assigned to a String
, it turns into a String
cookie (I don't want to know what that tastes like). That's a start, but in CookieMonster.eat(noCookie).length()
, there is no context, so it turns into Object
which doesn't have a length()
method, and that doesn't compile.
We fix that by putting a bound on C
. For both of the calls to eat
to compile, the bound needs to be a supertype of String
, and have a length()
method. There is one such type: CharSequence
. So we replace the type variable <C>
by <C extends CharSequence>
. In the first call eat("cookie")
must return an empty String
, and in the second call eat("")
must return a CharSequence
with negative length:
Congrats to the 9 Java monsters who found the solution; see the comments below.
9 comments
My Cookie Monster:
That was tough, thanks for the challenge!
As I am pretty sure you cannot get a genuine java.lang.String with negative length, let’s use some unchecked generics magic to have the method return a String in the first (to avoid ClassCastExceptions) and another CharSequence with negative length in the second case.
Using a StringBuilder instead of the Segment would also work (use two threads to get the count negative), or just implement your own implementation of CharSequence. But I think this is the shortest solution :-)
Nice one, but not too tricky. The key term here is “type inference”. We make the monster return a String for each “real” cookie and only a (“negative”) CharSequence when given no cookie - that one can be customized to have negative length. Since the “noCookie” variable is declared to be String, the compiler will do the casting.
Both casts give unchecked casts warnings:
Another great puzzle. Thanks, keep them coming.