关于 Java 中私有静态嵌套类的合成访问器的 Eclipse 警告?

Posted

技术标签:

【中文标题】关于 Java 中私有静态嵌套类的合成访问器的 Eclipse 警告?【英文标题】:Eclipse warning about synthetic accessor for private static nested classes in Java? 【发布时间】:2010-10-29 14:13:55 【问题描述】:

我的同事建议让一些 Eclipse 代码格式和警告设置更加严格。这些更改中的大多数都是有意义的,但我在 Java 中得到了这个奇怪的警告。这是一些重现“问题”的测试代码:

package com.example.bugs;

public class WeirdInnerClassJavaWarning 
    private static class InnerClass
    
        public void doSomething() 
    

    final private InnerClass anInstance;

    
        this.anInstance = new InnerClass();   // !!!
        this.anInstance.doSomething();
    

// using "this.anInstance" instead of "anInstance" prevents another warning,
// Unqualified access to the field WeirdInnerClassJavaWarning.anInstance    

带有 !!! 的行在 Eclipse 中使用我的新警告设置给我这个警告:

访问封闭构造函数 WeirdInnerClassJavaWarning.InnerClass() 由合成访问器模拟 方法。提高其知名度将 提高你的表现。

这是什么意思?当我将“私有静态类”更改为“受保护的静态类”时,警告消失了,这对我来说毫无意义。


编辑:我终于找到了“正确”的修复方法。这里真正的问题似乎是这个嵌套的私有静态类缺少公共构造函数。这一调整消除了警告:

package com.example.bugs;

public class WeirdInnerClassJavaWarning 
    private static class InnerClass
    
        public void doSomething() 
        public InnerClass() 
    

    final private InnerClass anInstance;

    
        this.anInstance = new InnerClass();
        this.anInstance.doSomething();
    

我希望这个类是一个私有的嵌套类(所以没有其他类可以访问它,包括封闭类的子类)并且我希望它是一个静态类。

我仍然不明白为什么使嵌套类受保护而不是私有是解决“问题”的另一种方法,但这可能是 Eclipse 的一个怪癖/错误。

(抱歉,为了更清楚,我应该将其称为 NestedClass 而不是 InnerClass。)

【问题讨论】:

我不明白一点:拥有一个不能被其他人使用的内部类有什么用处?我想我会把这个类放在一个单独的类文件中,让其他程序员选择它是否对其他作用域有用。 【参考方案1】:

您不能从 WeirdInnerClassJavaWarning 实例化 InnerClass。它是私有的,JVM 不会让你这样做,但 Java 语言(出于某种原因)会。

因此,javac 会在 InnerClass 中创建一个额外的方法,该方法只会返回 new InnerClass(),因此允许您从 WeirdInnerClassJavaWarning 创建 InnerClass 实例。

我不认为你真的需要摆脱它,因为性能下降会非常小。不过,如果你真的想,你可以。

【讨论】:

+1 性能提升在现代 JVM 上应该是完全无关紧要的。 Eclipse 只是在这里过于热情,我会完全关闭该警告。 “您不能从 WeirdInnerClassJavaWarning 实例化 InnerClass。它是私有的,JVM 不允许您这样做,但 Java 语言(出于某种原因)可以。”没有构造函数存在,Java 语言将自动创建一个无参数的公共构造函数,不存在。 Eclipse 警告说,这种魔力正在发生。 我猜这个构造函数在私有内部类的情况下是私有的。否则这个警告没有任何意义。【参考方案2】:

您应该能够通过使用默认范围而不是私有或受保护范围来摆脱它,即

static class InnerClass ...

还值得注意的是,我将光标放在带有警告的代码行上并按 ctrl-1,Eclipse 可能会自动为您修复此问题。

【讨论】:

谢谢,但我不希望这样,它应该对封闭类以外的类不可见。 在这种情况下,您可能想要取消警告,因为 Eclipse 的性能导向警告与您的需求无关。 相关配置为“访问封闭类型的不可访问成员”【参考方案3】:

您可以按如下方式摆脱警告:

package com.example.bugs;

public class WeirdInnerClassJavaWarning 
    private static class InnerClass 
        protected InnerClass()   // This constructor makes the warning go away
        public void doSomething() 
    

    final private InnerClass anInstance;
    
        this.anInstance = new InnerClass(); 
        this.anInstance.doSomething();
    

正如其他人所说,Eclipse 抱怨是因为没有显式构造函数的私有类无法从外部实例化,除非通过 Java 编译器创建的合成方法。如果你获取你的代码,编译它,然后用jad (*) 反编译它,你会得到以下(重新格式化):

public class Test 
  private static class InnerClass 
    public void doSomething() 
    // DEFAULT CONSTRUCTOR GENERATED BY COMPILER:
    private InnerClass() 

    // SYNTHETIC METHOD GENERATED BY THE JAVA COMPILER:    
    InnerClass(InnerClass innerclass) 
      this();
    
  

  public Test() 
    anInstance.doSomething();
  

  // Your instance initialization as modified by the compiler:
  private final InnerClass anInstance = new InnerClass(null);

如果添加受保护的构造函数,则不需要合成代码。我想,理论上,合成代码比使用公共或受保护构造函数的非合成代码慢一点。

(*) 对于 jad,我链接到了一个 Wikipedia 页面……托管此程序的域已过期,但 Wikipedia 链接到另一个我自己没有测试过的域。我知道还有其他(可能是更新的)反编译器,但这是我开始使用的。注意:在反编译最近的 Java 类文件时它抱怨,但它仍然做得很好。

【讨论】:

我假设您在我进行编辑之前正在发帖。无论如何,这似乎是正确的答案。 是的,你和我同时打字。我已经扩展了我的答案,还解释了为什么你会收到 Eclipse 警告。 +1 关于反编译器,我从来不知道有这样的事情。听起来我应该为 Eclipse 提交一个错误,他们应该更改此警告的文本​​,以便更清楚这意味着什么以及应该修复什么。 除非这是一个 J2ME 项目,否则最好的解决方案是禁用 Eclipse 中的警告并编写代码以符合您的意图。在 JIT 之后,性能影响将可以忽略不计或为零。 @Darron & Co. 我看不出表演应该从何而来。编译器生成合成方法,因此在运行时它应该看起来就像您自己放置它时一样。还是我错过了什么?【参考方案4】:

我仍然不明白为什么让嵌套类受保护而不是私有是解决“问题”的另一种方法,但这可能是 Eclipse 的一个怪癖/错误

这不是 Eclipse 的怪癖/错误,只是 Java 的一个特性。 Java Language Specification, 8.8.9 说:

...如果类被声明为protected,则默认构造函数被隐式赋予访问修饰符protected ...

【讨论】:

...和protected 和private 对封闭类有不同的影响?我认为唯一的区别是如何授予子类的访问权限。 它在源代码级别(编译器)没有影响,但在字节代码级别(虚拟机)。据我所知,JVM 不知道嵌套类,这就是为什么你会为它们获得一个额外的 .class 文件。编译器必须创建合成方法,以便“外部”类可以访问嵌套类的私有成员(在运行时)。 (顺便说一句:受保护的还包括包访问,这足以避免警告)(顺便说一句:按照 JLS 的定义,静态嵌套类不是内部类...)【参考方案5】:

顺便说一句,关闭警告的设置在“代码样式”下的Java错误/警告页面中,被称为:

访问封闭类型的不可访问成员

【讨论】:

对我来说已经设置为忽略,但我仍然看到警告。也许朱诺号发生了一些变化。 是否有可能在项目特定设置中设置为警告? 我知道这看起来多么可悲,但是 - 如果警告仍然存在,请在某处添加一些字符并保存文件。警告将在之后消失。 在 Kepler 中我可以忽略此警告【参考方案6】:

为了帮助大家,如果您在问题中使用原始类代码,您会得到以下结果:

javac -XD-printflat WeirdInnerClassJavaWarning.java -d tmp

原始输出,编译器添加了 cmets。注意添加了合成包私有类和构造函数。

public class WeirdInnerClassJavaWarning 
    
    

    public WeirdInnerClassJavaWarning() 
        super();
    
    
    
    private final WeirdInnerClassJavaWarning$InnerClass anInstance;
    
        this.anInstance = new WeirdInnerClassJavaWarning$InnerClass(null);
        this.anInstance.doSomething();
    


class WeirdInnerClassJavaWarning$InnerClass 

    /*synthetic*/ WeirdInnerClassJavaWarning$InnerClass(WeirdInnerClassJavaWarning$1 x0) 
        this();
    

    private WeirdInnerClassJavaWarning$InnerClass() 
        super();
    

    public void doSomething() 
    


/*synthetic*/ class WeirdInnerClassJavaWarning$1 

【讨论】:

以上是关于关于 Java 中私有静态嵌套类的合成访问器的 Eclipse 警告?的主要内容,如果未能解决你的问题,请参考以下文章

私有的嵌套类(内部或静态)是不是可能具有具有公共访问权限的方法?

Java中私有静态嵌套类中的访问修饰符

如何访问嵌套类的私有成员?

java内部私有类的构造函数

通过对象引用访问实例变量的静态嵌套类的 Java 示例 [重复]

java内部类的静态嵌套类