Recently I wrote a post about a catalyst method in Java – an issue where an existence of a method that is never actually invoked can affect the code execution. The trick was that the way compiler created bytecode for static method was ambiguous for this given code snippet and therefore JVM resolved it incorrectly.
The code snippet I presented before publishing was simplified multiple times to make is as readable as possible. At some point the following simplification was suggested:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: |
public static class BaseSerializer {
static <E> Object serialize(E obj) {
return "BaseSerializer " + obj.toString();
}
}
public static class CatalystSerializer extends BaseSerializer {
static <E> String serialize(E obj) {
return "CatalystSerializer " + obj.toString();
}
}
public static class AdvancedSerializer extends CatalystSerializer {
static <E extends Serializable> String serialize(E obj) {
return "AdvancedSerializer " + obj.toString();
}
}
public static void main(String[] args) {
System.out.println(AdvancedSerializer.serialize("ABC"));
System.out.println(AdvancedSerializer.serialize(new Object()));
}
|
…but did not worked – the bug was missing. So that is a puzzler: why the snippet above works fine while the code in a previous post did not (see the code for comparison). Where is the difference that made the issue appear its ugly head?
Before I give away the answer let me say what did not caused the difference. It’s obviously not because of the classes names I used (hahaha!) or the content of each of the methods. It is not because of using a different interface – Serializable in this code does the same thing as Comparable in the previous one. Finally it is not because of using Lists or any other collections – no matter how many flaws they might have it is not what contributed to the puzzle. So what is it?
If you read the previous post it won’t be a surprise to you: it’s because of type erasure. In original code the JVM got confused because two different for compiler static methods were saved as the same bytecode. This was possible due to type erasure – the generic typing was the only difference between those methods and that got ‘lost in translation’.
The situation in the code above is slightly different: you might expect that after type erasing both calls in main to AdvancedSerializer.serialize() and CatalystSerializer.serialize() will look the same and JVM will make an error again. In fact the first one will get compiled to ‘String AdvancedSerializer.serialize(Serializable)’, while second one due to a compiler brilliance will become ‘String AdvancedSerializer.serialize(Object)’!
What’s the lesson here? Well, puzzle itself is quite interesting but… I just can’t think of any
Maybe only this: it’s good to have brilliant Java compiler, huh?


