Tuesday, December 22, 2009

Java inner class peculiarity

I recently ran across an obscure Java factoid that will probably never come in handy. I found it when researching PMD rules.

Suppose I have an inner class as follows:

1 package com.examples;

3 public class Outer {

5   public void test() {
6     Inner ic = new Inner();
7   }

9   public class Inner {
10     private Inner() {
11     }
12   }
13 }


Even though the constructor for the inner class Inner is private, a package-access constructor is created by the compiler as a side-effect when you call the private constructor at line 6 in the test method.

There are a couple of ways to verify this. First, you can just look at the generated class file Outer$Inner.class. You'll find two lines, one for the private constructor that you have written, and another which is a "synthetic"* constructor:

..
private Outer$Inner(com.examples.Outer arg0);
..
synthetic Outer$Inner(com.examples.Outer arg0, com.examples.Outer.Inner arg1);


If you are not convinced, you can run through the constructors using reflection, as shown in this example class:

1 package com.examples;
2 import java.lang.reflect.*;

4 public class InnerTester {
5   public static void main(String[] args) throws ClassNotFoundException {
6     InnerTester.printConstructors("com.examples.Outer$Inner");
7   }
8   public static void printConstructors(String str) throws ClassNotFoundException {
9      Class c = Class.forName(str);
10     Constructor[] cs = c.getDeclaredConstructors();
11     for (int i = 0; i < cs.length; i++) {
12       System.out.println(cs[i].toString());
13     }
14   }
15}

Running this program produces two lines of output:

com.examples.Outer$Inner(com.examples.Outer,com.examples.Outer$Inner)
private com.examples.Outer$Inner(com.examples.Outer)


If you comment out line 6 in Outer.java and recompile it, the output of this program is just one line:

private com.examples.Outer$Inner(com.examples.Outer)


You can look at the class file, Outer$Inner.class, and check that the package access constructor is gone, there, too.

There's a rule warning against calling a private constructor of an inner class like this, because the constructor is effectively given package access. PMD suggests using a factory method instead of creating the instance directly in the outer class, or just making the constructor public, to solve the problem.

I can't imagine any situation in which this would be a problem unless you're using reflection to call constructors.


* The Sun tutorial on Java reflection has a brief discussion of synthetic (compiler-generated) constructors.

Sunday, December 20, 2009

classes that override equals should override hashCode

I was talking to a developer the other day who admitted to me that he had only recently found out that in Java, if you override the Object equals method, you should override the hashCode method also. He had been introduced to this practice by a checkstyle rule.

This surprised me for a couple of reasons. This developer is not a newcomer to Java, and it's the kind of thing I'd expect an experienced developer to know. I know about it myself from my first forays into Java. It's documented in the API for Object:
Note that it is generally necessary to override the hashCode method whenever this method [equals] is overridden, so as to maintain the general contract for the hashCode method, which states that equal objects must have equal hash codes.

But I suppose it's the kind of thing you might miss learning if you come into Java from another object-oriented language, and don't do a thorough job of learning the peculiarities of Java.

The other thing that surprised me was the fact that he admitted it at all. Developers rarely confess to ignorance. I guess that admitting a weakness is just too dangerous in this highly competitive field. I tend to be pretty open about my own gaps in knowledge, but I suspect it doesn't help me any, and in some cases it may hurt. Have you ever said "gee I didn't know X" and gotten a snooty look down the nose from someone? Not the kind of experience you want to repeat!

Friday, December 11, 2009

debugging tools

I recently talked to a developer who admitted that he preferred using print statements to debug than any of the newer debugging tools, like what's available in Eclipse. This is the kind of statement that would probably knock my socks off if it weren't for the fact that almost nothing surprises me anymore!

I disagreed, but didn't argue it with him. Print statements are the worst, last option, in my world. There are just a few times when they are appropriate - for example, if you want to grep through values in a large loop. And of course in some cases print statements are the only option (on a production machine, for example).

But really, who doesn't just love a debugger? It's useful for 99% of the problems I have to solve.