Skip to main content

Java Puzzler

I bought the book Java Puzzlers by Josh Bloch and Neal Gafter a few years ago and enjoyed it. If you aren't familiar with it, it covers rare, odd and usually counter-intuitive edge cases of Java in a brain-teaser style of presentation. For both a Java user and an analytical mind its a fascinating book.

Well today I stumbled onto a puzzler of my own and thought I'd share. Can you tell me what the main method of this class will output?
1 : public class Parser {
2 :
3 : private static <T> T parse(String s, Class<T> type) {
4 : // Simple implementation only supports long primitive
5 : if(type == long.class) {
6 : return (T) Long.parseLong(s);
7 : }
8 : throw new UnsupportedOperationException(
9 : "parse() only supports long right now!");
10: }
11:
12: public static void main(String[] args) {
13: System.out.println(parse("1234", long.class).equals(1234));
14: }
15: }

So what gets written to standard output? If you said 'true', then you'd be wrong. If you said 'false', then you'd also be wrong! The code above won't even compile! There are a few features of Java that are colliding here to give odd results, including generics, type erasure, primitive types and autoboxing.

So what happens exactly? You'll get a compile error like this:
Error:line (6) inconvertible types
found : long
required: T
The error is on the return statement line of the parse() method. Long.parseLong() returns a primitive long, but since a parameter type can only represent an Object type, you can't cast a primitive to an parameter type. Also, it appear auto-boxing doesn't work in this case, so it fails with a compiler error.

But wait, doesn't the code explicitly use the Class object that represents the long primitive, long.class? But see 'long.class' is syntactic sugar for the constant field Long.TYPE which is of the parameterized type Class<Long> since type variables only support using Objects.

So realizing this, we can change the return statement to use a Long instead of a long:
    return (T) Long.valueOf(s);
Now the code will compile, but something is still wrong because it prints out 'false'.

Knowing that the return type of parse("1234", long.class) is Long, take another look at the puzzle. Can you see the problem? This one is subtle. The numeric literal '1234' that is inside the call to equals() needs to be auto-boxed and since it is a literal int, it is boxed into a Integer. But an Integer object isn't considered equal to a Long object because they are different types, so equals() returns false.

So it a few interesting features combine for some strange behavior. Hope you enjoyed this puzzle!

Comments

Neal Gafter said…
Most of the puzzlers in our book aren't rare; in fact, they're taken out of actual problems people have encountered while writing production code. However, the issues are distilled to their essence in the book.
My apologies if I mischaracterized it. It is a great book and one I recommend to other programmers as a must-read because its so informative and accessible.

Popular posts from this blog

Paperless

I've been slowly going paperless over the past decade. The first step on my journey started in 2000 when I signed up to use a payment service, PayTrust, to receive my incoming bills, scan them, and put them online for me to pay. The next major step was probably when I got a digital camera to replace my traditional film cameras. It might not be considered a "paperless" use case, but it has lead to very little hardcopies over the years as monitors and HDTV with screensavers and AppleTVs have become so beautiful.  Back to the paperless office, my next big step was eFileing my taxes but that didn't come until about 5 years later. Then suddenly about two years ago, I hit a real shift in my desire to go completely paperless when I got my iPad and installed Evernote.

digital notes...
If you aren't familiar with Evernote its an excellent app, available on all the major desktop and mobile OSes, that makes note-taking and organizing really simple. The killer feature is …

3D Photo Viewer for Looking Glass

I created my first Chrome extension, which is now live on the Chrome Web Store! It's built for the Looking Glass, a holographic display that let's you view three-dimensional objects without glasses. I've also opened the source to the extension on GitHub.

The Chrome extension allows you to view Facebook's "3D Photos", a feature they added in 2018 for displaying photos that include a depth map like those from phones with dual cameras, such as Apple's "Portrait Mode".

Getting Started To use the extension, connect your Looking Glass to your computer, navigate to Facebook and open the viewer from the extension's popup menu. This will open a browser window on the Looking Glass display's screen in fullscreen mode.
Once the viewer is open, the extension watches for any 3D Photo files being downloaded, so browse around Facebook looking for 3D Photos.  I recommend some of the Facebook groups dedicated to this topic like Facebook 3D Photos, and th…

Simplifying logging with Maven and SLF4J (Part 2)

So in my previous post I explained how to simplify your logging with Maven and SLF4J. If you haven't read it yet, please do before reading more.  Since then I've discovered an easier and cleaner way to remove the secondary frameworks from your Maven dependency tree.

Here's a revised overview of the steps:

Decided which logging framework will be your primary, aka who will actually write to your log file.Define the dependency scope of all the secondary frameworks to be 'provided'.Configure your project to depend on drop-in replacements of each secondary framework from SLF4J.
Define secondary frameworks as provided
Use the dependencyManagement section for this. Its used when you might have a dependency transitively.
Add dependency on SLF4J Add the following to your pom.xml
Conclusion
So now in only 3 steps you can redirect all your logging to your primary logging framework without changing a line of code!