eclipse 合成访问器方法警告

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4501324/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-19 15:31:52  来源:igfitidea点击:

Synthetic accessor method warning

javaeclipseperformancejava-synthetic-methods

提问by Rites

I've made some new warning settings in eclipse. With these new settings I'm facing a strange warning. After reading I got to know what it is but couldn't find a way to remove it.

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

Here is my problem with the sample code

这是我的示例代码问题

public class Test {
    private String testString;

    public void performAction() {

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

The line with ** gives me a warning in eclipse

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

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

Problem is, I don't want to change the access modifier of testString. Also, don't want to create a getter for it.

问题是,我不想更改testString. 另外,不想为它创建一个吸气剂。

What change should be done?

应该做哪些改变?



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

Lines with **gives me the same warning.

**给了我同样的警告。

采纳答案by rsp

In your second example it is not necessary to access testButtondirectly; you can access it by retrieving the source of the action event.

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

For the resetButton()method you can add an argument to pass the object to act upon, if you've done that it is not such a big problem lowering its access restrictions.

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

回答by dimo414

What is a "synthetic" method?

什么是“合成”方法?

Starting from the Methodclass (and it's parent, Member) we learn that synthetic members are "introduced by the compiler", and that JLS §13.1will tell us more. It notes:

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

A construct emitted by a Java compiler must be marked as syntheticif it does not correspond to a construct declared explicitly or implicitly in source code

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

Since this section is discussing binary compatibility the JVMS is also worth referencing, and JVMS §4.7.8adds a little more context:

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

A class member that does not appear in the source code must be marked using a Syntheticattribute, or else it must have its ACC_SYNTHETICflag set. The only exceptions to this requirement are compiler-generated methods which are not considered implementation artifacts....

The Syntheticattribute was introduced in JDK 1.1 to support nested classes and interfaces.

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

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

In other words, "synthetic" methods are an implementation artifact that the Java compiler introduces in order to support language features that the JVM itself does not support.

换句话说,“合成”方法是 Java 编译器为了支持 JVM 本身不支持的语言特性而引入的实现工件。

What's the problem?

有什么问题?

You're running into one such case; you're attempting to access a privatefield of a class from an anonymous inner class. The Java language permits this but the JVM doesn't support it, and so the Java compiler generates a synthetic method that exposes the privatefield to the inner class. This is safe because the compiler doesn't allow any other classes to call this method, however it does introduce two (small) issues:

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

  1. Additional methods are being declared. This shouldn't be a problem for the vast majority of use cases, but if you're working in a constrained environment like Android andare generating a lot of these synthetic methods you may run into issues.
  2. Access to this field is done indirectly through the synthetic method, rather than directly. This too shouldn't be a problem except for highly performance-sensitive use cases. If you wouldn't want to use a getter method here for performance reasons, you wouldn't want a synthetic getter method either. This is rarely an issue in practice.
  1. 正在声明其他方法。对于绝大多数用例来说,这应该不是问题,但是如果您在像 Android 这样的受限环境中工作并且正在生成大量这些合成方法,您可能会遇到问题。
  2. 对该字段的访问是通过合成方法间接完成的,而不是直接进行的。除了对性能高度敏感的用例外,这也不应该成为问题。如果出于性能原因不想在这里使用 getter 方法,那么您也不需要合成的 getter 方法。这在实践中很少成为问题。

In short, they're really not bad. Unless you have a concrete reason to avoid synthetic methods (i.e. you've determined conclusively they are a bottleneck in your application) you should just let the compiler generate them as it sees fit. Consider turning off the Eclipse warning if it's going to bother you.

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

What should I do about them?

我该怎么办?

If you really want to prevent the compiler from generating synthetic methods you have a couple of options:

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

Option 1: Change the permissions

选项 1:更改权限

Package-private or protectedfields are accessible to inner classes directly. Especially for something like a Swing application this ought to be fine. But you say you want to avoid this, so on we go.

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

Option 2: Create a getter

选项 2:创建一个吸气剂

Leave your fields alone, but explicitly create a protectedor publicgetter, and use that instead. This is essentially what the compiler ends up doing for you automatically, but now you have direct control over the method's behavior.

不用管你的字段,但明确地创建一个protectedpublicgetter,并使用它来代替。这基本上是编译器最终自动为您做的事情,但现在您可以直接控制方法的行为。

Option 3: Use a local variable and share the reference with both classes

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

This is more code but it's my personal favorite since you're making the relationship between the inner and outer class explicit.

这是更多的代码,但它是我个人的最爱,因为您正在明确内部和外部类之间的关系。

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

This isn't always actually what you want to do. For instance if testButtoncan change to point to a different object later, you'll need to rebuild your ActionListeneragain (though that's also nicely more explicit, so arguably this is a feature), but I consider it the option that most clearly demonstrates its intent.

这实际上并不总是您想要做的。例如,如果以后testButton可以更改为指向不同的对象,您将需要ActionListener再次重建您的对象(尽管这也更加明确,因此可以说这是一个功能),但我认为这是最清楚地表明其意图的选项。



Aside on thread-safety

除了线程安全

Your example Testclass is not thread-safe - testStringis being set in a separate Threadbut you're not synchronizing on that assignment. Marking testStringas volatilewould be sufficient to ensure all threads see the update. The Syntheticexample doesn't have this problem since testButtonis only set in the constructor, but since that's the case it would be advisable to mark testButtonas final.

您的示例Test类不是线程安全的 -testString被单独设置,Thread但您没有同步该分配。标记testStringvolatile足以确保所有线程看到更新。该Synthetic示例没有这个问题,因为testButton它只在构造函数中设置,但既然是这种情况,建议将其标记testButtonfinal.

回答by NPE

Given the context (you assign to the variable once as part of a fairly expensive operation), I don't think you need to do anything.

鉴于上下文(作为相当昂贵的操作的一部分,您一次分配给变量),我认为您不需要做任何事情。

回答by Amir Raminfar

I think the problem is that you are setting a String on the parent class. This will suffer in performance because the Thread needs to look up to see where that variable is again. I think a cleaner approach is using Callable which returns a String and then do .get() or something that returns the result. After getting the result you can set the data back on to the parent class.

我认为问题在于您在父类上设置了一个字符串。这将影响性能,因为 Thread 需要查找以再次查看该变量的位置。我认为更简洁的方法是使用 Callable ,它返回一个字符串,然后执行 .get() 或返回结果的操作。获得结果后,您可以将数据设置回父类。

The idea is you want to make sure the Thread only does one thing and only one thing instead of setting variables on other classes. This is a cleaner approach and probably faster because the inner Thread doesn't access anything outside of itself. Which mean less locking. :)

这个想法是你想确保 Thread 只做一件事并且只做一件事,而不是在其他类上设置变量。这是一种更简洁的方法,并且可能更快,因为内部 Thread 不访问自身之外的任何内容。这意味着更少的锁定。:)

回答by orsg

This is one of the rare cases where Java's default visibility (also called "package private") is of use.

这是使用 Java 的默认可见性(也称为“包私有”)的罕见情况之一。

public class Test {
    /* no private */ String testString;

    public void performAction() {

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

This will do the following:

这将执行以下操作:

  • testStringis now available to all classes in the same package as the outer class (Test).
  • As inner classes are actually generated as OuterClassPackage.OuterClassName$InnerClassName, they also reside in the same package. They can therefore access this field directly.
  • In contrast to making this field protected, the default visibility will not make this field available to subclasses (except when they are in the same package of course). You therefore don't pollute your API for external users.
  • testString现在可用于与外部类 ( Test)相同的包中的所有类。
  • 由于内部类实际上是作为 生成的OuterClassPackage.OuterClassName$InnerClassName,因此它们也驻留在同一个包中。因此,他们可以直接访问该字段。
  • 与使此字段相反protected,默认可见性不会使此字段可用于子类(当然,它们在同一个包中时除外)。因此,您不会为外部用户污染您的 API。

When using private, javac will instead generate a synthetic accessor, which itself is just a getter method with Java's default visibility as well. So it basically does the same thing, except with a minimal overhead of an additional method.

使用时private, javac 将生成一个合成访问器,它本身也是一个具有 Java 默认可见性的 getter 方法。所以它基本上做同样的事情,除了额外方法的最小开销。