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