Arnt Gulbrandsen
About meAbout this blog

I break compatibility

What is java? In a way, my compiler defines java as the language used to write the twenty thousand classes in the JDK library, Jsoup or whatever else gradle fetches to build a test. While I do read the specification, test-driven development is called test-driven for a reason.

A test drove me into a problem yesterday and my head hurts. I've encountered that problem before but escaped for various reasons, this time I have to confront it. The problem involves a one-line function that just accepts an argument, casts it to a subclass and returns it. Javac has compiled that as aload_1; areturn, which means push the first argument onto the stack and then return it. Javac would ordinarily include a checkcast to make sure that that first argument actually has the function's return type, but didn't in this particular case.

Taken together, this tiny function and its callers convert an Object to more-specific class without type checking. The caller gets an arbitrary Object from a call that returns String, and this is legal java. I'll skip the details of why this is legal, they just make me sad and angry. The gist is that String a = new Object(); is illegal, but if you introduce an intermediate step using generics, you can get something that's a String in the source code and an Object at runtime, and sooner or later you'll get a strange, surprising exception.

The java generics FAQ is 350 pages long.

I might choose to break compatibility and make all variants of String a = new Object(); either fail at compile time or throw an exception in a sensible location, even the variants that involve generics. Then the test will pass and let's not focus on the specification. I might. I'm not sure.

Am I able to tolerate a known specification violation, even if it's a small one? It feels like approximating π to 3.