合成访问器方法警告
我在 eclipse 中做了一些新的警告设置。有了这些新设置,我面临着一个奇怪的警告。读完后我知道它是什么,但找不到删除它的方法。
这是我的示例代码的问题带有
public class Test {
private String testString;
public void performAction() {
new Thread( new Runnable() {
@Override
public void run() {
testString = "initialize"; // **
}
});
}
}
** 的行在 eclipse 中给了我一个警告
Read access to enclosing field Test.testString is emulated by a synthetic accessor method.
Increasing its visibility will improve your performance.
问题是,我不想更改 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");
}
}
带有 **
的行给了我同样的警告。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
什么是“综合”方法?
从
方法开始
类(及其父类,Member
)我们了解到合成成员是“由编译器引入”,并且 JLS §13.1 将告诉我们更多信息。它指出:由于本节讨论二进制兼容性,因此 JVMS 也值得参考,并且< a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.8" rel="noreferrer">JVMS §4.7.8 添加了更多上下文:
换句话说,“合成”方法是 Java 编译器为了支持 JVM 本身不支持的语言功能而引入的实现工件。
有什么问题吗?
你正遇到这样的情况;您正在尝试从匿名内部类访问类的
private
字段。 Java 语言允许这样做,但 JVM 不支持,因此 Java 编译器生成一个合成方法,将private
字段公开给内部类。这是安全的,因为编译器不允许任何其他类调用此方法,但是它确实引入了两个(小)问题:简而言之,他们确实不差。除非您有具体的理由避免合成方法(即您已经确定它们是应用程序中的瓶颈),否则您应该让编译器按照其认为合适的方式生成它们。如果 Eclipse 警告对您造成困扰,请考虑将其关闭。
我该怎么办?
如果您确实想阻止编译器生成合成方法,您有几个选择:
选项 1:更改权限
内部类可以直接访问包私有或
protected
字段。特别是对于像 Swing 应用程序这样的东西,这应该没问题。但你说你想避免这种情况,所以我们继续吧。选项 2:创建 getter
保留您的字段,但显式创建一个
protected
或public
getter,并使用它。这本质上是编译器最终自动为您做的事情,但现在您可以直接控制该方法的行为。选项 3:使用局部变量并与两个类共享引用
这是更多代码,但这是我个人最喜欢的,因为您使内部类和外部类之间的关系变得明确。
这实际上并不总是您想要做的。例如,如果
testButton
稍后可以更改为指向不同的对象,则您需要再次重建ActionListener
(尽管这也更加明确,所以可以说这是一个功能),但我认为它是最清楚地表明其意图的选项。除了线程安全之外,
您的示例
Test
类不是线程安全的 -testString
正在单独的Thread
中设置,但您没有同步在那项任务上。将testString
标记为volatile
足以确保所有线程都能看到更新。Synthetic
示例不存在此问题,因为testButton
仅在构造函数中设置,但既然是这种情况,建议标记testButton
> 作为最终
。What is a "synthetic" method?
Starting from the
Method
class (and it's parent,Member
) we learn that synthetic members are "introduced by the compiler", and that JLS §13.1 will tell us more. It notes:Since this section is discussing binary compatibility the JVMS is also worth referencing, and JVMS §4.7.8 adds a little more context:
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.
What's the problem?
You're running into one such case; you're attempting to access a
private
field 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 theprivate
field 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: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.
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
Package-private or
protected
fields 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.Option 2: Create a getter
Leave your fields alone, but explicitly create a
protected
orpublic
getter, 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.Option 3: Use a local variable and share the reference with both classes
This is more code but it's my personal favorite since you're making the relationship between the inner and outer class explicit.
This isn't always actually what you want to do. For instance if
testButton
can change to point to a different object later, you'll need to rebuild yourActionListener
again (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.Aside on thread-safety
Your example
Test
class is not thread-safe -testString
is being set in a separateThread
but you're not synchronizing on that assignment. MarkingtestString
asvolatile
would be sufficient to ensure all threads see the update. TheSynthetic
example doesn't have this problem sincetestButton
is only set in the constructor, but since that's the case it would be advisable to marktestButton
asfinal
.在第二个示例中,不必直接访问
testButton
;您可以通过检索操作事件的来源来访问它。对于
resetButton()
方法,您可以添加一个参数来传递要执行操作的对象,如果您这样做了,那么降低其访问限制并不是一个大问题。In your second example it is not necessary to access
testButton
directly; you can access it by retrieving the source of the action event.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.考虑到上下文(作为相当昂贵的操作的一部分,您分配给变量一次),我认为您不需要做任何事情。
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.
我认为问题在于您在父类上设置了一个字符串。这会影响性能,因为线程需要再次查找该变量的位置。我认为更干净的方法是使用 Callable 返回一个 String,然后执行 .get() 或返回结果的操作。获得结果后,您可以将数据设置回父类。
这个想法是你想确保线程只做一件事,并且只做一件事情,而不是在其他类上设置变量。这是一种更干净的方法,而且可能更快,因为内部线程不会访问其自身之外的任何内容。这意味着更少的锁定。 :)
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.
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. :)
这是使用 Java 的默认可见性(也称为“包私有”)的罕见情况之一。
这将执行以下操作:
testString
现在可用于与外部类 (Test
) 相同的包中的所有类。OuterClassPackage.OuterClassName$InnerClassName
生成的,因此它们也驻留在同一个包中。因此他们可以直接访问该字段。当使用
private
时,javac将生成一个合成访问器,它本身只是一个具有Java默认可见性的getter方法。所以它基本上做同样的事情,除了额外方法的开销最小。This is one of the rare cases where Java's default visibility (also called "package private") is of use.
This will do the following:
testString
is now available to all classes in the same package as the outer class (Test
).OuterClassPackage.OuterClassName$InnerClassName
, they also reside in the same package. They can therefore access this field directly.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.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.