JLS tweaks

Brian Goetz brian.goetz at oracle.com
Fri Mar 14 14:57:57 UTC 2014


Regardless, "the JVM allows it, so the language should allow it" is 
barking up the wrong tree.  The JVM and language have deeply different 
requirements, constraints, goals, and programmer audiences (the 
programmer audience for the JVM is mostly compiler writers.)  The JVM 
has this flexibility not because we believe its a good idea to not 
initialize final fields, but because offline compilation is a better 
place to enforce this guideline than runtime.

This "improvement" was deemed a bad idea in 2006 and is still a bad idea:

   https://bugs.openjdk.java.net/browse/JDK-4093999

Just because a lot of other people had the same bad idea, doesn't mean 
its a good idea.

As far as I can see, the only real argument raised in favor of this 
feature is "the language already allows partially-constructed instances 
to be exposed to arbitrary code, how much worse would it be if it 
exposed even-less-constructed objects?"  But this argument is a pretty 
silly one (it basically says that unless one can enforce a safety goal 
100%, the goal has no legitimacy.)

As I've said before, I don't even like that instances methods can be 
called from constructors, since they may still expose an object that has 
not been completely constructed.  For example:

class A implements Listener {
      public A() {
          // Do A's initialization
          someEventSource.registerListener(this);  // really bad idea
      }

      // called when event source sends us an event
      public void onEvent(Event e) {
          ...
      }
}

Now, this might seem harmless, right?  A has already been initialized, 
right?  Wrong.  Suppose someone subclasses B, adds invariants, and 
overrides onEvent:

class B extends A {
     private final Logger logger;

     public B() {
          super();
          this.logger = new Logger();
     }

     @Override
     public void onEvent(Event e) {
         logger.log("Event: " + e);  // XXX
         super.onEvent(e);
     }
}

Now suppose that the event source sends a message really soon after 
registration.  B.onEvent() could see logger == null at line XXX.  Ooops! 
  What went wrong?  A exposed a partially constructed B from its 
constructor, because the B initialization had not yet run when A's 
constructor registered itself as a listener, even though it thought it 
was all done initializing.  B had no idea that A was a time bomb waiting 
to blow up.  All because A was allowed to expose 'this' before it was 
finished constructing.

As I said, I'd be more in favor of tightening the rules, to disallow 
this-escapes like this, rather than broaden the conditions under which 
partially constucted 'this' references can escape.  But we can't do 
that, so at least we shouldn't make the problem worse.

This stuff is trickier than it looks.  We should err on the side of 
making this stuff harder.



On 3/14/2014 9:50 AM, David M. Lloyd wrote:
> On 03/13/2014 05:00 PM, Archie Cobbs wrote:
>> On Thu, Mar 13, 2014 at 5:21 AM, Peter Levart <peter.levart at gmail.com
>> <mailto:peter.levart at gmail.com>> wrote:
>>
>>     On 03/13/2014 11:08 AM, Peter Levart wrote:
>>
>>         I don't see this as any more complex or unsafe as the rules for
>>         final fields. But I don't know if it is compatible with JVM
>> rules.
>>
>>
>>     Does JVM have any rules for final instance fields regarding their
>>     definitive assignment at all non-exceptional exit paths of the
>>     constructor?
>>
>>
>> My understanding is that the JVM doesn't care how many times you assign
>> a final field in a constructor, it only cares that you don't try to
>> assign it from within a non-constructor method.
> [...]
>
>> Note also that the JVM spec specifically allows a constructor to assign
>> final fields before invoking super()/this().
>
> I think you're mistaken on both of these points... can you quote a
> section of the JLS that says this?
>


More information about the compiler-dev mailing list