This follows up on a previous question:
The accepted answer gave a work-around that will be used if a better solution cannot be found.
This question distills the original into a simple, easy to replicate situation, without proprietary code. Hence, this provides full code details of the original.
Sadly, the java versions used cannot be changed easily. This problem comes from legacy product support. The strange path of building each file one at a time mimics the build process of the custom code written on top of our product, written on top of a legacy api.
Apologies for length & detail of this post.
The Questions Are:
This post attempts to provide different details to the original post, and hopefully the code will demonstrate the strange behaviour.
Using these steps, the original post was reduced to a trivial 3 class problem that replicated the original issue. I would like to know if anyone can shed some more light on this and provide a good explanation for what we found.
The Problem Is:
public
classes, one of which contains a static
class.static
class needs an enclosing instance, which is just not true.The exact build process has been encompassed in a batch file, so it is replicable.
Below, the full source of the 3 java files, and the batch file. Also the runtime output of the batch file.
package demo;
public class Outer
{
/** this causes a problem when instantiated in Superclass and in BrokenChild **/
public static class Static_1
{
}
/** this causes no problem as it is not instantiated in SuperClass, only in BrokenChild **/
public static class Static_2
{
}
}
package demo;
import demo.Outer;
import demo.Outer.Static_1;
import demo.Outer.Static_2;
public class SuperClass
{
public void breaksBuild()
{
// instantiating Static_1 here prevents BrokenChild from instantiating Static_1 in a later build
Object f = new Static_1();
}
// public void breaksBuildIfUncommented()
// {
// Object f = new Static_2();
// }
}
package demo;
import demo.SuperClass;
import demo.Outer;
import demo.Outer.Static_1;
import demo.Outer.Static_2;
public class BrokenChild extends SuperClass
{
/** method broken because Static_1 is instantiated in SuperClass */
public void breaksBuild()
{
// commenting the below line allows this class to build
Object f = new Static_1();
}
/** method works because Static_2 is not instantiated in SuperClass */
public void buildsProperly()
{
// this instance can remain, since it is not instantiated in SuperClass
Object f = new Static_2();
}
}
@goto start
Demonstrates both a working build, and a broken build of BrokenChild.
setup for both scenarios:
- Outer is built with either JVC or JAVAC
scenario #1 - BrokenChild build FAILS:
- SuperClass is built using JVC
- BrokenChild does not build using JAVAC
scenario #2 - BrokenChild build works:
- SuperClass is built using JAVAC
- BrokenChild does build using JAVAC
:start
@prompt $s$s$s$s$g
:: init folders
@if exist .\build rd .\build /s /q
@md .\build
@echo ------------------------------------------------------------------------------
@echo Build Outer using JVC or JAVAC (does not matter)
jvc.exe /nologo /d .\build .\src\demo\Outer.java
:: building with JAVAC still breaks the BrokenChild build
:: javac.exe -d .\build .\src\demo\Outer.java
@echo.
@echo ------------------------------------------------------------------------------
@echo Build SuperClass using JVC (building with JAVAC does not break the BrokenChild build)
jvc.exe /nologo /d .\build -cp:p .\build .\src\demo\SuperClass.java
:: building with JAVAC does not break the BrokenChild build
:: javac.exe -d .\build -classpath .\build src\demo\SuperClass.java
@echo.
@echo ------------------------------------------------------------------------------
@echo BrokenChild build FAILS using JAVAC
javac.exe -d .\build -classpath .\build src\demo\BrokenChild.java
@echo.
@echo.
@echo Show files
dir .\build\*.class /s /b
@echo.
@echo ------------------------------------------------------------------------------
@echo BrokenChild build WORKS using JVC
jvc.exe /nologo /d .\build -cp:p .\build .\src\demo\BrokenChild.java
@echo.
@echo.
@echo Show files
dir .\build\*.class /s /b
@echo.
@echo ------------------------------------------------------------------------------
@prompt $p$g
@pause
Build Outer using JVC or JAVAC (does not matter)
>jvc.exe /nologo /d .\build .\src\demo\Outer.java
------------------------------------------------------------------------------
Build SuperClass using JVC (building with JAVAC does not break the BrokenChild b
uild)
>jvc.exe /nologo /d .\build -cp:p .\build .\src\demo\SuperClass.java
------------------------------------------------------------------------------
BrokenChild build FAILS using JAVAC
>javac.exe -d .\build -classpath .\build src\demo\BrokenChild.java
src\demo\BrokenChild.java:15: error: an enclosing instance that contains Outer.S
tatic_1 is required
Object f = new Static_1();
^
Show files
>dir .\build\*.class /s /b
C:\jvc_bug\build\demo\Outer$Static_1.class
C:\jvc_bug\build\demo\Outer$Static_2.class
C:\jvc_bug\build\demo\Outer.class
C:\jvc_bug\build\demo\SuperClass.class
------------------------------------------------------------------------------
BrokenChild build WORKS using JVC
>jvc.exe /nologo /d .\build -cp:p .\build .\src\demo\BrokenChild.java
Show files
>dir .\build\*.class /s /b
C:\jvc_bug\build\demo\BrokenChild.class
C:\jvc_bug\build\demo\Outer$Static_1.class
C:\jvc_bug\build\demo\Outer$Static_2.class
C:\jvc_bug\build\demo\Outer.class
C:\jvc_bug\build\demo\SuperClass.class
I believe the output makes the issue self-evident. Am happy to provide further details as required.
It looks like a compiler bug in JDK 1.4.2 / 1.5 to me. I can't see any obvious explanation for the compiler calling that an error.
I tried searching the Java Bugs Database for something similar but couldn't find anything. But that may mean that this is an alternative manifestation of some other bug.
The fact that people can't reproduce this with JDK 1.6 or 1.7 most likely means that the problem (whatever it was) has been fixed for a long time.
So what can you do about it? Very little, I suspect. If you have an Oracle Java support contract (that covers JDK 1.4), then you could fire off a support request to them and ask for an explanation. But (assuming that this is a bug) they are unlikely to offer you a fix.
If you wanted to investigate further, I would suggest a couple of things (if you haven't already tried them):
Change the BrokenChild
class so that you don't import
those classes. Refer to them by their fully qualified names instead. Repeat for the SuperClass
class ...
Use javap
to examine the relevant ".class" files to see if there is anything unexpected about them.
Try compiling the individual files with a later (Oracle/Sun) java compiler; e.g. old SuperClass
with newer BrokenChild
and vice versa.
(I doubt any of these will reveal anything interesting ... but they might.)
All I can say is that I hope that you / your company are charging someone LOTS OF MONEY for supporting this product on a legacy platform.