Java Solaris Communities Sun Store Join SDN My Profile Why Join?
 
Bug Database
Bug Detail
Quick Lists
Top 25 Bugs
Top 25 RFE's
Recently Closed Bugs
Printable Page Printable Page


Bug Database
Bug ID: 4926251
Votes 1
Synopsis instance variable initializers in named classes can throw checked exceptions
Category java:specification
Reported Against 1.4.2
Release Fixed 1.5(tiger-rc)
State 10-Fix Delivered, bug
Priority: 3-Medium
Related Bugs
Submit Date 22-SEP-2003
Description




FULL PRODUCT VERSION :
java version "1.4.2"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2-b28)
Java HotSpot(TM) Client VM (build 1.4.2-b28, mixed mode)

FULL OS VERSION :
 customer  Windows XP [Version 5.1.2600]

A DESCRIPTION OF THE PROBLEM :
I am sure this is a documentation problem in the JLS, but am filing it against the compiler because if the JLS were interpreted literally the compiler would in fact have a bug.

Amazingly, I have not been able to find any previously submitted bugs or any discussion of this issue whatsoever on the JDC.

While it is generally understood that instance initializers (as defined in JLS �8.6, "Instance Initializers") can throw checked exceptions if the checked exception is assignable to an exception class in the throws clause of every constructor in the class (which assumes that at least one constructor is declared), the same cannot be said for instance variable initializers. Here it is important to distinguish between instance initializers and instance initialization methods (or <init> methods). The sections of the JLS that I will cite in the remainder of this bug report are clearly discussing the former, which is a free standing block of code in compilation units as opposed to the methods invoked by the JVM to initialize objects. The code from instance initializers is included in <init> methods, but these are two very different entities.

The specification in question is the following paragraph from �8.3.2, "Initialization of Fields" in the Second Edition of the JLS.

     "It is a compile-time error if the evaluation of a variable initializer for a static field or for an instance variable of a named class (or of an interface) can complete abruptly with a checked exception."

This specification is repeated in �11.2, "Compile-Time Checking of Exceptions":

    �Static initializers, class variable initializers, and instance initializers or instance variable initializers within named classes and interfaces, must not result in a checked exception; if one does, a compile-time error occurs. No such restriction applies to instance initializers or instance variable initializers within anonymous classes.�

This paragraph from �11.2, "Compile-Time Checking of Exceptions" involves an unrelated error that has been corrected at http://java.sun.com/docs/books/jls/clarifications-2-2nd-ed.html as follows.

    �JLS 11.2
    �The last two paragraphs of section 11.2 on page 221 should be amended to read. This resolves an apparent discrepancy with JLS 8.6 :

    �Static initializers, class variable initializers, and instance variable initializers within named classes and interfaces, must not result in a checked exception; if one does, a compile-time error occurs. No such restriction applies to instance initializers or instance variable initializers within anonymous classes.

    �An instance initializer of a named class may not throw a checked exception unless that exception or one of its superclasses is explicitly declared in the throws clause of each constructor of its class and the class has at least one explicitly declared constructor. An instance initializer in an anonymous class can throw any exceptions.�

Here I must point out that only the last paragraph of that section is being corrected. The second paragraph is an addition to the printed specification.

In all three JLS passages we find the following two sentences:

�8.3.2, "Initialization of Fields"
�It is a compile-time error if the evaluation of ... an instance variable of a named class (or of an interface) can complete abruptly with a checked exception.�

�11.2, "Compile-Time Checking of Exceptions" and http://java.sun.com/docs/books/jls/clarifications-2-2nd-ed.html:
�...instance variable initializers within named classes and interfaces, must not result in a checked exception; if one does, a compile-time error occurs.�

These specification could not be any clearer. Instance variable initializers in named classes cannot throw checked exceptions, but that is not how the language actually works. Consider the following program:

    class Test {
       String s = doSomething();
       Test() throws Exception { }
       String doSomething() throws Exception {
           return "throws a checked exception";
       }
    }


The method invocation doSomething() is clearly an instance variable initializer in a named class that throws a checked exception.

This program compiles in the 1.4.2 release as well as in all previous releases back to the 1.2 release in which it breaks the compiler. Only in 1.1.8 and earlier releases does it generate the following compiler error:

Test.java:2: Exception java.lang.Exception can't be thrown in initializer.
   String s = doSomething();
                         ^
1 error

It is clear that the language changed sometime between the 1.1.8 and the 1.3 releases. The change is that instance variable initializers are behaving the same instance initializers in this regard. They can throw a checked exception as long as the exception is assignable to an exception class in the throws clause of every constructor in the class (which again assumes that at least one constructor is declared).

Further confirmation of this behavior is possible by simply deleting the throws clause in the constructor (or deleting the entire constructor for that matter). If this is done, all releases back to the 1.2 release generate the following compiler error:

Test.java:2: unreported exception java.lang.Exception; must be caught or declared to be thrown
   String s = doSomething();
                         ^
1 error

This is confirmation in and of itself that the compiler behavior is deliberate.

Is this a compiler error or a documentation problem? At this point the answer almost has to be that the JLS must be changed. Otherwise, there is a backwards compatibility problem. Yet there is a strong argument against permanently adopting this change to the language. (I really do not care what is done but present this as a devil's advocate argument.)

Language designers usually opt for the greatest flexibility as long as there is no downside. In this case that would have been specifying that instance variable initializers for named classes can throw any checked exception as long as it is named in the throws clause of every constructor in the class (the same rule as is applied to instance initialization blocks). There is no significant difference between the two. Class instance creation expressions do not actually invoke a constructor, they invoke <init> methods. These special initialization methods include code from instance variable initializers (transformed into assignment statements) and instance initialization blocks, as well code from the corresponding constructor. The <init> method copies the throws clause from the corresponding constructor. Thus adding checked exceptions thrown in instance variable initializers and instance initialization blocks to the throws clause of every constructor in the class is an obvious means of documenting that instantiating the class (i.e., evaluating a class instance creation expression) throws checked exceptions above and beyond what the constructor throws. Where is the downside? The downside is that there is no possibility of catching the exception thrown in an instance variable initializer. Thus allowing them to throw checked exceptions encourages a coding style in which checked exceptions are added to the throws clause of every constructor in the class simply because the programmer either does not realize that the exception could be handled in an instance initializer or is to lazy to do so.




REPRODUCIBILITY :
This bug can be reproduced always.
(Incident Review ID: 199248) 
======================================================================
Work Around
N/A
Evaluation
The spec should probably be modified to say that an instance variable initializer in a named class may throw a checked exception if it is declared in the throws clause of every constructor.

  xxxxx@xxxxx   2003-09-29

Wow, a spec bug description written by someone who actually understands
what is going on! Miracles never cease. 

Yes, in this case there is no real choice but to change the spec to fit the
implementation. In any case, it is cleaner that way.


  xxxxx@xxxxx   2003-10-14
Comments
  
  Include a link with my name & email   

Submitted On 26-SEP-2003
javarules.com
Boy I feel stupid about this. Once this was accepted as a 
bug, I went back to study the problem much closer. (At least 
I had enough humility to wait until then to pursue it any 
further. I was just incredulous that no one else had noticed 
it, and assumed there was something I was overlooking.) 
Why I wrote the following is something of a mystery to me.

"This program compiles in the 1.4.2 release as well as in all 
previous releases back to the 1.2 release in which it breaks 
the compiler. Only in 1.1.8 and earlier releases does it 
generate the following compiler error:"

I just got it wrong. As it turns out, I can't figure out how to 
get my 1.2.0 compiler to execute at all. (I assume it is a 
registry problem, but can't figure it out.) In the excitement 
of stumbling across this bug I thought this little program 
was breaking the compiler. I still can't figure out why I 
thought it compiled in 1.3 releases. It definitely does not 
compile in either 1.3.1 and 1.3.1_016. This only appears to 
have been a problem since the 1.4.0 release. "My bad."



PLEASE NOTE: JDK6 is formerly known as Project Mustang