3

这是对上一个问题的跟进:

接受的答案提供了一种解决方法,如果找不到更好的解决方案,将使用该解决方法。

这个问题将原始问题提炼成一个简单、易于复制的情况,没有专有代码。因此,这提供了原始的完整代码细节。

可悲的是,使用的 java 版本不能轻易更改。此问题来自旧产品支持。一次构建每个文件的奇怪路径模仿了在我们的产品之上编写的自定义代码的构建过程,这些代码是在遗留 api 之上编写的。

为这篇文章的长度和细节道歉。


问题是:

  • 为什么 java 编译器无法识别给定的类是静态的
  • 为什么 java 编译器坚持使用封闭类的实例?

这篇文章试图提供与原帖不同的细节,希望代码能展示出奇怪的行为。

使用这些步骤,原始帖子被简化为复制原始问题的微不足道的 3 类问题。我想知道是否有人可以对此有所了解,并为我们的发现提供一个很好的解释。


问题是:

  1. 3个public类,其中一个包含一个static类。
  2. 这些类之间的继承关系。
  3. 使用 Microsoft JVC 和 Sun javac 的组合构建(至少在 1.4.2_18 和一些 1.5 版本上)。
  4. 最终构建中断,抱怨static该类需要一个封闭的实例,这是不正确的。

确切的构建过程已包含在批处理文件中,因此它是可复制的。

下面是 3 个 java 文件和批处理文件的完整源代码。也是批处理文件的运行时输出。


外部.java

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
    {
    }
}

超类.java

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();
//  }

}

BrokenChild.java

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();
    }

}

演示.bat

@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

运行 demo.bat 的输出

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

我相信输出使问题不言而喻。很高兴根据需要提供更多详细信息。

4

1 回答 1

2

对我来说,它看起来像是 JDK 1.4.2 / 1.5 中的编译器错误。对于编译器调用错误,我看不到任何明显的解释。

我尝试在 Java Bugs Database 中搜索类似的东西,但找不到任何东西。但这可能意味着这是其他错误的另一种表现形式。

人们无法用 JDK 1.6 或 1.7 重现这一事实很可能意味着问题(无论是什么)已经修复了很长时间。

所以你对此能做些什么?很少,我怀疑。如果您有 Oracle Java 支持合同(涵盖 JDK 1.4),那么您可以向他们发出支持请求并要求解释。但是(假设这一个错误)他们不太可能为您提供修复。


如果您想进一步调查,我会建议几件事(如果您还没有尝试过):

  • 更改BrokenChild课程,以便您不使用import这些课程。改为使用它们的完全限定名称来引用它们。重复SuperClass上课...

  • 用于javap检查相关的“.class”文件,看看它们是否有任何意外。

  • 尝试使用更高版本的(Oracle/Sun)java 编译器编译单个文件;例如旧SuperClass的和新的BrokenChild反之亦然

(我怀疑这些中的任何一个都会揭示任何有趣的东西......但他们可能会。)


我只能说,我希望您/您的公司向某人收取很多钱,以在旧平台上支持该产品。

于 2013-08-07T10:01:23.717 回答