合成访问器方法警告

Rit*_*tes 14 java eclipse performance java-synthetic-methods

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

这是我的示例代码问题

public class Test {
    private String testString;

    public void performAction() {

        new Thread( new Runnable() {
            @Override
            public void run() {
                testString = "initialize"; // **
            }
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

**的行在eclipse中给我一个警告

Read access to enclosing field Test.testString is emulated by a synthetic accessor method. 
Increasing its visibility will improve your performance.
Run Code Online (Sandbox Code Playgroud)

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

应该做些什么改变?


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");
    }
}
Run Code Online (Sandbox Code Playgroud)

线条**给了我同样的警告.

dim*_*414 27

什么是"合成"方法?

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

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

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

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

Synthetic属性是在JDK 1.1中引入的,以支持嵌套类和接口.

换句话说,"合成"方法是Java编译器引入的实现工件,以便支持JVM本身不支持的语言功能.

有什么问题?

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

  1. 正在宣布其他方法.对于绝大多数用例而言,这应该不是问题,但如果您在像Android这样的受限环境中工作并且正在生成大量这些合成方法,则可能会遇到问题.
  2. 访问此字段是通过合成方法间接完成的,而不是直接访问.除了对性能敏感的用例外,这也不应成为问题.如果由于性能原因你不想在这里使用getter方法,那么你也不需要合成的getter方法.这在实践中很少成为问题.

简而言之,他们真的不错.除非你有一个具体的理由要避免合成方法(即你已经确定它们是你的应用程序的瓶颈),你应该让编译器按照它认为合适的方式生成它们.如果它打扰你,请考虑关闭Eclipse警告.

我该怎么办?

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

选项1:更改权限

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

选项2:创建一个getter

保持字段不变,但显式创建一个protectedpublicgetter,然后使用它.这基本上是编译器最终自动为您做的事情,但现在您可以直接控制方法的行为.

选项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;
}
Run Code Online (Sandbox Code Playgroud)

这并不总是你想要做的事情.例如,如果以后testButton可以更改为指向另一个对象,则需要ActionListener再次重建(虽然这也很明确,所以可以说这是一个特性),但我认为它是最清楚地展示其意图的选项.


除了线程安全

您的示例Test类不是线程安全的 - testString正在单独设置,Thread但您没有在该分配上进行同步.标记testStringvolatile足以确保所有线程都能看到更新.该Synthetic示例没有此问题,因为testButton仅在构造函数中设置,但由于这种情况,建议将其标记testButtonfinal.


rsp*_*rsp 3

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

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