Community
Participate
Working Groups
The "private" modifier is dropped from constructors of private inner classes (both default and explicitly declared ones). Perhaps this was a deliberate choice since it eliminates the need for a synthetic constructor, but it causes the following class to behave differently than it does if compiled with javac. import java.lang.reflect.Constructor; import java.io.ByteArrayOutputStream; import java.io.ObjectOutputStream; import java.io.ByteArrayInputStream; import java.io.ObjectInputStream; import java.io.Serializable; public class Foo { public static void main(String[] args) throws Throwable { Constructor[] ctors = Bar.class.getDeclaredConstructors(); for (int i = 0; i < ctors.length; ++i) { System.out.println(ctors[i]); } ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(new Zot()); oos.close(); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); System.out.println(ois.readObject()); } private static class Bar { private Bar() { } } private static class Zot extends Bar implements Serializable { } } If the inner class Bar is changed to default access while leaving its constructor private, the behavior is then equivalent to javac's.
We reset the private bit in MethodScope line 124. Kent, is this required?
Philippe, do you remember why we did this?
This indeed was done to simplify innerclass synthetic access emulation. Note that the synthetic emulation is spec'ed nowhere, thus our decision is as valid as javac's. There are numerous cases where our synthetic constructions will not match theirs, and this is only one of them.
Your synthetics certainly don't need to match javac's, but that isn't the issue. An explicitly declared constructor has had its access modifier changed. (Or, if a default constructor has to be generated, its access does not match its class as JLS 8.8.7 requires.) This is a pretty small point, unlikely to cause any real trouble if you decide to leave it alone. But I still think it's a bug and not just a matter that the spec is silent on.
We ran into this problem when compiling Mauve with the eclipse compiler. It does affect serialization. (In our particular case it occurred with an implicit constructor, but the idea is the same...) FWIW all the other compilers I know of (jikes, gcj, gcjx, javac) generate a private constructor as specified, and then a package-private accessor constructor.
This behavior is optional, but always occurring as soon as compliance >= 1.3. I believe we did match some behavior back then. Maybe this is obsolete nowadays (need to check further). Note that our emulation is more efficient in those case, but I agree it clears a private modifier which isn't super nice. Now, Tom, I wonder why you mention serialization. There is no way you can assume serializing from on compiler to another one. There are too many subtle differences, like this one. Unless internals were spec'ed, serialization must ensure using the same reference on both ends, or you are asking for trouble.
Yeah, I understand about relying on serialization compatibility across compilers. And the issue is probably a minor one as it requires a serializable member class with a private constructor. And yet we managed to hit this :-) But suppose you do understand serialization compatibility and are trying to make it work -- ie you specify serialVersionUID. In this case changing a constructor from private to package-private could break things, because (as I understand it) it changes which constructor is invoked when deserializing. Our test case looks a lot like the test in this PR, but it had an implicit constructor. I think we (meaning mauve/classpath/etc hackers) all had the same interpretation of the JLS, namely that the implicit constructor would take its access from its declaring class -- not just in the compiler's view but in the generated class file.
*** Bug 77473 has been marked as a duplicate of this bug. ***
After investigation, I must apologize to all. Indeed, the behavior I was describing was only meant to occur within local types, to ease emulation (and I will further check this again). Changing visibility for member type constructors feel totally wrong, and I will fix it asap. Thanks for the reminder.
Added InnerEmulationTest#test124
Checked the local type scenario. I aligned our behavior with javac. At 1.3 compliance, the private modifier on local type constructor is kept, and emulation is performed. From 1.4 on, it is cleared. Added InnerEmulationTest#test125. Fixed.
Verified for 3.2 M6 using build I20060328-0010