综合访问器方法警告

我在 eclipse 中做了一些新的警告设置。有了这些新设置,我面临着一个奇怪的警告。阅读后,我知道它是什么,但找不到删除它的方法。

这是我对示例代码的问题

public class Test {
    private String testString;

    public void performAction() {

        new Thread( new Runnable() {
            @Override
            public void run() {
                testString = "initialize"; // **
            }
        });
    }
}

带有**的行在日食中给我一个警告

Read access to enclosing field Test.testString is emulated by a synthetic accessor method. 
Increasing its visibility will improve your performance.

问题是,我不想更改 的访问修饰符。另外,不想为它创建一个getter。testString

应该做些什么改变?


More descriptive example 

public class Synthetic
{
    private JButton testButton;

    public Synthetic()
    {
        testButton = new JButton("Run");
        testButton.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent ae)
                {
                    /* Sample code */
                    if( testButton.getText().equals("Pause") ) {
                        resetButton(); // **    
                    } else if( testButton.getText().equals("Run") ) {
                        testButton.setText("Pause"); // **  
                    }

                }
            }
        );
    }

    public void reset() {
        //Some operations
        resetButton();
    }

    private void resetButton() {
        testButton.setText("Run");
    }
}

行与给了我同样的警告。**


答案 1

什么是“合成”方法?

Method 类(以及它的父类,Member)开始,我们了解到合成成员是“由编译器引入的”,JLS §13.1 将告诉我们更多。它指出:

如果 Java 编译器发出的构造与源代码中显式或隐式声明的构造不对应,则必须将其标记为综合构造

由于本节讨论的是二进制兼容性,因此JVMS也值得参考,JVMS §4.7.8增加了更多的上下文:

未出现在源代码中的类成员必须使用属性进行标记,否则必须设置其标志。此要求的唯一例外是编译器生成的方法,这些方法不被视为实现工件。SyntheticACC_SYNTHETIC

该属性在 JDK 1.1 中引入,以支持嵌套类和接口。Synthetic

换句话说,“综合”方法是 Java 编译器引入的一种实现工件,用于支持 JVM 本身不支持的语言功能。

怎么了?

你遇到了一个这样的案例;您正在尝试从匿名内部类访问类的字段。Java语言允许这样做,但JVM不支持它,因此Java编译器生成一个合成方法,将字段公开给内部类。这是安全的,因为编译器不允许任何其他类调用此方法,但是它确实引入了两个(小)问题:privateprivate

  1. 正在声明其他方法。对于绝大多数用例来说,这应该不是问题,但是如果您在Android等受限环境中工作并且正在生成许多这些合成方法,则可能会遇到问题。
  2. 对此字段的访问是通过合成方法间接完成的,而不是直接完成的。除了对性能高度敏感的用例外,这也不应该是一个问题。如果出于性能原因不想在此处使用 getter 方法,则也不会想要合成 getter 方法。这在实践中很少成为问题。

简而言之,他们真的还不错。除非你有具体的理由避免使用综合方法(即你已经最终确定它们是应用程序中的瓶颈),否则你应该让编译器在它认为合适的时候生成它们。考虑关闭 Eclipse 警告,如果它会打扰你。

我该怎么办?

如果你真的想阻止编译器生成综合方法,你有几个选择:

选项 1:更改权限

包私有或字段可直接由内部类访问。特别是对于像Swing应用程序这样的东西,这应该没问题。但是你说你想避免这种情况,所以我们走了。protected

选项 2:创建 getter

不要理会您的字段,但要显式创建一个或 getter,并改用它。这实质上是编译器最终自动为您执行的操作,但现在您可以直接控制方法的行为。protectedpublic

选项 3:使用局部变量并与两个类共享引用

这是更多的代码,但它是我个人最喜欢的,因为你使内部和外部类之间的关系变得明确。

public Synthetic() {
  // Create final local instance - will be reachable by the inner class
  final JButton testButton = new JButton("Run");
  testButton.addActionListener(
      new ActionListener() {
        public void actionPerformed(ActionEvent ae) {
          /* Sample code */
          if( testButton.getText().equals("Pause") ) {
            resetButton();
          } else if( testButton.getText().equals("Run") ) {
            testButton.setText("Pause");
          }
        }
      });
  // associate same instance with outer class - this.testButton can be final too
  this.testButton = testButton;
}

这并不总是你真正想要做的。例如,如果以后可以更改为指向其他对象,则需要再次重新构建(尽管这也更明确,因此可以说这是一个功能),但我认为它是最清楚地表明其意图的选项。testButtonActionListener


撇开螺纹安全

您的示例类不是线程安全的 - 在单独的环境中设置,但您没有在该分配上进行同步。标记为足以确保所有线程都看到更新。该示例没有此问题,因为仅在构造函数中设置,但由于是这种情况,因此建议标记为 。TesttestStringThreadtestStringvolatileSynthetictestButtontestButtonfinal


答案 2

在第二个示例中,没有必要直接访问;您可以通过检索操作事件的源来访问它。testButton

对于该方法,您可以添加一个参数来传递要对其执行操作的对象,如果您已经这样做了,那么降低其访问限制并不是一个大问题。resetButton()


推荐