Question: What does the following code display?
import java.awt.Color; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JLayeredPane; import javax.swing.JPanel; public class Demo { public static void main(String[] args) { JFrame frame = new JFrame(); JLayeredPane container = new JLayeredPane(); frame.setContentPane(container); JPanel bottom = new JPanel(); bottom.setBounds(0, 0, 400, 200); bottom.setBackground(Color.WHITE); container.add(bottom); // default level is 0 JLabel intLabel = new JLabel("I'm an int"); intLabel.setBounds(0, 10, 400, 30); int layer = 10; container.add(intLabel, layer); JLabel integerLabel = new JLabel("I'm an Integer"); integerLabel.setBounds(0, 50, 400, 30); container.add(integerLabel, new Integer(10)); JLabel literalLabel = new JLabel("I'm a literal"); literalLabel.setBounds(0, 100, 400, 30); container.add(literalLabel, 10); JLabel longLabel = new JLabel("I'm a long"); longLabel.setBounds(0, 150, 400, 30); container.add(longLabel, new Long(10)); frame.setSize(400, 200); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } }
Answer
Explanation
If you open docs for JLayeredPane, you can see the following code snippets:
layeredPane.add(child, new Integer(10));
layeredPaneParent.setLayer(child, 10);
You can see Integer
there and literal 10
here. Subconsciously, you think: who cares, 10
is the same thing as new Integer(10)
because Java has autoboxing, right? WRONG!
The only right way to interact with layeredPane.add
is an Integer that you box yourself. Let’s take a peek at its source code…
protected void addImpl(Component comp, Object constraints, int index) { int layer = DEFAULT_LAYER.intValue(); int pos; if(constraints instanceof Integer) { // A-ha! layer = ((Integer)constraints).intValue(); setLayer(comp, layer); } else layer = getLayer(comp); pos = insertIndexForLayer(layer, index); super.addImpl(comp, constraints, pos); comp.validate(); comp.repaint(); validateOptimizedDrawing(); }
Ironically, even though it uses int
internally, it wouldn’t recognize it as a parameter.
Conclusion
Autoboxing works, except for when it doesn’t. Be defensive with APIs (even core Java) and when something is wrong do try all those ideas that seem obviously crazy at first. When writing your own API that uses type identification, remember that int
and Integer
are completely different beasts and both need to be included.
This example is from Swing, but it can happen anywhere. I’ve tripped on this issue before when I was implementing a CRUD framework or renderers, but did not expect it in core library.
Update
As frimble pointed out at reddit, there is one more reason why it would never work. In addition to add(Component comp, Object constraints)
, Container
also has add(Component comp, int index)
.
Having two overloaded versions of a method, one of them accepting Integer
and another for int
, when both do completely different things, is even worse than what I criticized in the first place.
Rant: Yet another example of extremely poor design in core Java API. Just like Object.equals()
having specification that’s impossible to meet in a reasonable way or the hierarchy of java.util.Date
. They use Java to teach OOP. Apparently it’s just as good for teaching how NOT to program.