Software Development

4 out of 5 Java Developers Failed to Solve This Question

Results from the Java Deathmatch – A puzzle minigame for developers

A few months ago we released a new side project of ours with a minisite called Java Deathmatch, and since then over 20,000 developers have given it a try. The site features 20 multiple-choice Java questions and today after we’ve gathered stats from all the games that have been played we’re happy to share some of the results and solutions with you.

Overall we collected 61,872 answers, which gives us about 3,094 answers for each of the 20 questions. Each Java deathmatch session randomly chooses 5 questions and gives you 90 seconds to solve each one. Every question has 4 possible answers. We’ve been criticized that the questions are too hard but, well, it’s not called a deathmatch for no reason! Using those stats, we were able to determine which were the hardest questions, and which were the easiest. In this post we’d like to share the 5 toughest questions from this experiment and solve them together.

On average, 41% of attempted answers were correct, which is not bad at all. The live stats of the results and questions by index are available right here. The stats for this post are a snapshot from July 26th.
On average, 41% of attempted answers were correct, which is not bad at all. The live stats of the results and questions by index are available right here. The stats for this post are a snapshot from July 26th.

1. The toughest question of the Java deathmatch

Let’s start with the toughest nut to crack, a question we received from Alexandru-Constantin Bledea from Bucharest. And it’s a real brain teaser. Only 20% of the participants were able to solve this questions. This means if you would have chosen an answer at random – You’d probably have a better chance at hitting the right one. Java generics have this quality about them.

toughestquestion

Alright, so what do we have here? We have generics with type erasure involved, and a couple of exceptions. A few things to remember here:

  1. RuntimeException and SQLException both inherit from Exception, while RuntimeException is unchecked and SQLException is a checked exception.
  2. Java generics are not reified, meaning that in compile time, the generic type information is “lost” and treated as if the code is replaced with the type’s bound or with Object if it doesn’t exist. This is what you call type erasure.

Naively we’d expect line 7 to cause a compilation error since you can’t cast SQLException to RuntimeException, but that’s not the case. What happens is that T is replaced with Exception so we have:

throw (Exception) t;  // t is also an Exception

Since pleaseThrow expects an Exception, and T is replaced with Exception, the cast is eliminated as if it wasn’t written. We can see that in bytecode:

private pleaseThrow(Ljava/lang/Exception;)V throws java/lang/Exception
L0
LINENUMBER 8 L0
ALOAD 1
ATHROW
L1
LOCALVARIABLE this LTemp; L0 L1 0
// signature LTemp<TT;>;
// declaration: Temp<T>
LOCALVARIABLE t Ljava/lang/Exception; L0 L1 1
MAXSTACK = 1
MAXLOCALS = 2

Just for fun, we tried to see what the bytecode will look like without generics involved, and the cast appeared right before the ATHROW statement:

CHECKCAST java/lang/RuntimeException

Now that we’re convinced there’s no casting involved, we can scratch off these two answers:

  • “Compilation fails because we cannot cast SQLException to RuntimeException”
  • “Throws ClassCastException because SQLException is not instanceof RuntimeException”

So we throw a SQLException after all, and you’d expect it to get caught by the catch block and get its stack trace. Well, not really. This game is rigged. Turns out the compiler gets confused just as we do, and the code makes it think that the catch block is unreachable. For the unsuspecting bystander, there is no SQLException. The correct answer is that compilation fails because the compiler doesn’t expect a SQLException to be thrown from the try block – When in fact it does get thrown!

Thanks again Alexandru for sharing this question with us!

2. toString(), or not toString(), that is the question

With only 24% of correct answers, the following question was the runner up on the tough scale.

q9

This one is actually much more simple, just from looking at line 12 we can see that this code prints out m1 and m2, rather than, m1.name and m2.name. The tricky part here was remembering that when printing out a class, Java uses its toString method. The “name” field was artificially added. If you miss that and follow the rest of the code correctly, you might be tricked to choose m1 & new name.

This line sets both names to “m1”:

m1.name = m2.name = "m1";

Then callMe sets m2’s name to new name, and we’re done.

But this snippet will actually print out something like this, including the class name and hashcode:

MyClass@3d0bc85 & MyClass@7d08c1b7

And the correct answer would be “None of the above”.

3. Google Guava Sets

This question didn’t really require specific knowledge of Guava sets, but left most of the respondents confused. Only 25% answered it correctly, the same as choosing an answer at random.

q6

So what are we seeing here? We have a method that returns a set containing a “clique” of a person’s best friends. We see that there’s a loop that checks if a person has a best friend, and adds them to the results set. If a person indeed has a best friend, it repeats the process for them, so we end up having a set of best friends until we reach a person who doesn’t have a best friend OR that its best friend is already in the set. That last part might be a bit tricky – we can’t add a person who is already in the set so there’s no potential for an infinite loop.

The problem here is that we’re risking an out of memory exception. There’s no bound on the set so we can keep adding and adding people until we run out of memory.

By the way, if you’re into Google Guava, check out this post we wrote about some of the lesser known yet useful features about it.

4. Double brace initialization, lol wut?!

This one was one of the shortest questions, but it was enough to get most of the developers confused. Only 26% got it right.

q12

Not many developers are aware of this syntax that comes in handy when you need to initialize a constant collection, although some side-effects are included. Actually, this lack of popularity might be a good thing. So when the WAT?! effect wears off, you can see that we add an element to the list, and then try to print it out. Normally you’d expect it to print out [John] but double brace initialization has other plans in mind. What we see here is an anonymous class that is used to initialize the List. When it tries to print out NAMES, it actually comes out as null. Since the initializer wasn’t consumed yet and the list is empty.

You can read more about double brace initialization right here.

5. The curious case of the map at runtime

This one is another community-contributed question coming from Barak Yaish from Israel. Only 27% of the participants were able to solve this question.

q2

Alright, compute looks up a value in the map. If it’s null, it adds it and returns its value. Since the list is empty, “foo” doesn’t exist, v is null, and we map “foo” to a new ArrayList<Object>(). The ArrayList is empty, so it prints out [].

For the second line, “foo” does exist in the map so we evaluate the expression on the right. The ArrayList is cast to a List successfully, and “ber” is added to it. add returns true and that’s what it prints out.

The correct answer is [] true. Thanks again Barak for sharing this question with us!

Bonus: And the easiest question is…

This time we have a question coming from Peter Lawrey of OpenHFT who also blogs on Vanilla Java. Peter is on the top 50 list of StackOverflow and this time he moved over to the other side and asked a question that 76% of you got right.

easiestquestion

Answer C is simpler than A, B & D doesn’t compile.

Conclusion

From time to time we really like playing this kind of puzzles to sharpen our Java knowledge, but if you ever find yourself spending too much time on these puzzlers in your own codebase, it will probably be less than ideal. Especially if someone calls in the middle of the night to fix a critical production error. For this kind of situation, we’ve built Takipi for Java. Takipi is a Java agent that knows how to track uncaught exceptions, caught exceptions and log errors on servers in production. It lets you see the variable values that cause errors, all across the stack, and overlays them on your code.

Reference: 4 out of 5 Java Developers Failed to Solve This Question from our JCG partner Alex Zhitnitsky at the Takipi blog.

Alex Zhitnitsky

Alex is an engineer working with OverOps on a mission to help Java and Scala developers solve bugs in production and rid the world of buggy software. Passionate about all things tech, he is also the co-founder & lead of GDG Haifa, a local developer group. Alex holds a B.Sc from the Technion, Israel's Institute of Technology.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

12 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Hans Wurst
Hans Wurst
9 years ago

The solution to question 3 (“potential OOM”) is not convincing.

The question mandates that the complete person object tree is already in memory, as bestFriend is accessed directly by field – opposed to a method invocation, which could potentially fetch the object from off-memory (e.g. database).
Given that, the memory-consuming effect of adding all those person object references to a HashSet would be hardly noticable.

Ashokchakravarthi
Ashokchakravarthi
9 years ago

In bonus question. “than A, B & D doesn’t compile.”

It seems option A does compile. Please check that.

Rob
Rob
9 years ago

Ashokchakravarthi the punctuation is somewhat ambiguous. C is the correct answer because it’s simpler than A. B & D don’t compile.

Anon
Anon
9 years ago

The author meant to say 2 things in that one sentence- (a) C is simpler than A, and (b) options B&D do not compile.

KC Baltz
KC Baltz
9 years ago

Ashok, the comment about the bonus question is confusingly worded and I initially read it the same way you did. However, what they were trying to say is that “C is a simpler answer than A while B & D don’t compile”.

Karl
Karl
9 years ago

You are reading the explanation in a way that does not make sense. The statement “B & D doesn’t compile” is separate from the initial statement. Read it like this:

“Answer C is simpler than A. Answers B & D do not compile.”

nelda
9 years ago

They actually have a grammar error in their answer which makes the answer confusing. Replacing the comma with a period fixes the answer. “C is simpler than A. Answers B and D do not compile.” [doesn’t] refers to a singular subject, [do not] refers to a plural subject and makes it inclusive. ;-)

nelda
9 years ago

Like the puzzlers! But who in their right mind would write code like that in the first place?? (Excluding the Bonus question.)

But you make a good point. If you find yourself trying to be clever, stop! Cleverness only leads to hidden bugs, confusion, and unmaintainable code, as clearly shown in the examples. As software developers we do not get paid for cleverness. We get paid to write code and get it to work correctly the first time, since there may never be time to come back and re-do things a second time.

Shilpa
Shilpa
9 years ago

Interesting.

SameOne
SameOne
9 years ago

1. Note that one can simply break the javaScript execution in his browser and pause the StopWatch. Since we deal with developers, I’d say it makes the conclusions potentially biased.
2. in qustion 16, the regular expression String is not properly escaped, so the correct answer should be “Regular Expression is incorrect” or “does not compile”.

harshit
9 years ago

good set of interesting questions

Ansh
9 years ago

I still didn’t get it. Felling confused.

Back to top button