Java 在构造函数警告中泄漏这个
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3921616/
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
Leaking this in constructor warning
提问by asalamon74
I'd like to avoid (most of the) warnings of Netbeans 6.9.1, and I have a problem with the 'Leaking this in constructor'
warning.
我想避免 Netbeans 6.9.1 的(大部分)警告,但我对'Leaking this in constructor'
警告有问题。
I understand the problem, calling a method in the constructor and passing "this
" is dangerous, since "this
" may not have been fully initialized.
我理解这个问题,在构造函数中调用一个方法并传递“ this
”是危险的,因为“ this
”可能尚未完全初始化。
It was easy to fix the warning in my singleton classes, because the constructor is private and only called from the same class.
在我的单例类中修复警告很容易,因为构造函数是私有的,只能从同一个类中调用。
Old code (simplified):
旧代码(简化):
private Singleton() {
...
addWindowFocusListener(this);
}
public static Singleton getInstance() {
...
instance = new Singleton();
...
}
New code (simplified):
新代码(简化):
private Singleton() {
...
}
public static Singleton getInstance() {
...
instance = new Singleton();
addWindowFocusListener( instance );
...
}
This fix is not working if the constructor is public and can be called from other classes. How is it possible to fix the following code:
如果构造函数是公共的并且可以从其他类调用,则此修复程序不起作用。如何修复以下代码:
public class MyClass {
...
List<MyClass> instances = new ArrayList<MyClass>();
...
public MyClass() {
...
instances.add(this);
}
}
Of course I want a fix which does not require to modify all my codes using this class ( by calling an init method for instance).
当然,我想要一个不需要修改我使用这个类的所有代码的修复程序(例如通过调用 init 方法)。
采纳答案by chiccodoro
Since you make sure to put your instances.add(this)
at the end of the constructor you should IMHO be safe to tell the compiler to simply suppress the warning(*). A warning, by its nature, doesn't necessarily mean that there's something wrong, it just requires your attention.
由于您确保将您的instances.add(this)
放在构造函数的末尾,因此恕我直言,您应该安全地告诉编译器简单地取消警告(*)。就其性质而言,警告并不一定意味着有问题,它只是需要您注意。
If you know what you're doing you can use a @SuppressWarnings
annotation. Like Terrel mentioned in his comments, the following annotation does it as of NetBeans 6.9.1:
如果您知道自己在做什么,则可以使用@SuppressWarnings
注释。就像 Terrel 在他的评论中提到的那样,从 NetBeans 6.9.1 开始,以下注释是这样做的:
@SuppressWarnings("LeakingThisInConstructor")
(*) Update:As Isthar and Sergey pointed out there are cases where "leaking" constructor code can look perfectly safe (as in your question) and yet it is not. Are there more readers that can approve this? I am considering deleting this answer for the mentioned reasons.
(*) 更新:正如 Isthar 和 Sergey 指出的那样,在某些情况下,“泄漏”构造函数代码看起来非常安全(如您的问题),但事实并非如此。是否有更多的读者可以认可这一点?由于上述原因,我正在考虑删除此答案。
回答by Colin Hebert
The best options you have :
您拥有的最佳选择:
- Extract your
WindowFocusListener
part in another class (could also be inner or anonymous) . The best solution, this way each class has a specific purpose. - Ignore the warning message.
WindowFocusListener
在另一个类中提取您的部分(也可以是内部的或匿名的)。最好的解决方案,这样每个类都有特定的目的。- 忽略警告消息。
Using a singleton as a workaround for a leaky constructor is not really efficient.
使用单例作为泄漏构造函数的解决方法并不是很有效。
回答by Nate W.
This is a good case of where a Factory that created instances of your class would helpful. If a Factory was responsible for creating instances of your class, then you would have a centralized location where the constructor is called, and it would be trivial to add a required init()
method to your code.
这是一个很好的例子,说明创建类的实例的工厂会有所帮助。如果工厂负责创建您的类的实例,那么您将拥有一个调用构造函数的集中位置,并且将所需的init()
方法添加到您的代码中将是微不足道的。
Regarding your immediate solution, I would suggest that you move any calls that leak this
to the last line of your constructor, and then suppress them with an annotation once you've "proved" that it is safe to do so.
关于您的直接解决方案,我建议您将任何泄漏this
到构造函数的最后一行的调用移到构造函数的最后一行,然后在“证明”这样做是安全的后用注释抑制它们。
In IntelliJ IDEA, you can suppress this warning with the following comment right above the line://noinspection ThisEscapedInObjectConstruction
在 IntelliJ IDEA 中,您可以使用该行正上方的以下注释来抑制此警告://noinspection ThisEscapedInObjectConstruction
回答by CAB
The annotation @SuppressWarnings("LeakingThisInConstructor") applicable only to the class an not to the constructor itself.
注解 @SuppressWarnings("LeakingThisInConstructor") 仅适用于类而不适用于构造函数本身。
Solusion I would suggest: create private method init(){/* use this here*/} and call it from the constructor. The NetBeans won't warn you.
解决方案我建议:创建私有方法 init(){/* 在这里使用它*/} 并从构造函数调用它。NetBeans 不会警告您。
回答by Ron Stern
Using a nested class (as suggested by Colin) is probably your best option. Here's the pseudocode:
使用嵌套类(如 Colin 所建议的)可能是您最好的选择。这是伪代码:
private Singleton() {
...
}
public static Singleton getInstance() {
...
instance = new Singleton();
addWindowFocusListener( new MyListener() );
...
private class MyListener implements WindowFocusListener {
...
}
}
回答by Jeril Kuruvila
There is no need of separate listener class.
不需要单独的侦听器类。
public class Singleton implements WindowFocusListener {
private Singleton() {
...
}
private void init() {
addWindowFocusListener(this);
}
public static Singleton getInstance() {
...
if(instance != null) {
instance = new Singleton();
instance.init();
}
...
}
}
回答by Andrew
One can write:
一个人可以写:
addWindowFocusListener(Singleton.this);
This will prevent NB from showing the warning.
这将阻止 NB 显示警告。
回答by sage88
Say you originally had a class like this that used itself as an ActionListener and therefore you end up calling addActionListener(this) which generates the warning.
假设您最初有一个这样的类,它将自己用作 ActionListener,因此您最终调用了生成警告的 addActionListener(this)。
private class CloseWindow extends JFrame implements ActionListener {
public CloseWindow(String e) {
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setLayout(new BorderLayout());
JButton exitButton = new JButton("Close");
exitButton.addActionListener(this);
add(exitButton, BorderLayout.SOUTH);
}
@Override
public void actionPerformed(ActionEvent e) {
String actionCommand = e.getActionCommand();
if(actionCommand.equals("Close")) {
dispose();
}
}
}
As @Colin Hebert mentioned, you could separate the ActionListener out into its own class. Of course this would then require a reference to the JFrame that you want to call .dispose() on. If you'd prefer not to fill up your variable name space, and you want to be able to use the ActionListener for multiple JFrames, you could do it with getSource() to retrieve the button followed by a chain of getParent() calls to retrieve the Class that extends JFrame and then call getSuperclass to make sure it's a JFrame.
正如@Colin Hebert 提到的,您可以将 ActionListener 分成自己的类。当然,这将需要对要调用 .dispose() 的 JFrame 的引用。如果您不想填充变量名称空间,并且希望能够将 ActionListener 用于多个 JFrame,则可以使用 getSource() 来检索按钮,然后是一系列 getParent() 调用检索扩展 JFrame 的类,然后调用 getSuperclass 以确保它是 JFrame。
private class CloseWindow extends JFrame {
public CloseWindow(String e) {
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setLayout(new BorderLayout());
JButton exitButton = new JButton("Close");
exitButton.addActionListener(new ExitListener());
add(exitButton, BorderLayout.SOUTH);
}
}
private class ExitListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
String actionCommand = e.getActionCommand();
JButton sourceButton = (JButton)e.getSource();
Component frameCheck = sourceButton;
int i = 0;
String frameTest = "null";
Class<?> c;
while(!frameTest.equals("javax.swing.JFrame")) {
frameCheck = frameCheck.getParent();
c = frameCheck.getClass();
frameTest = c.getSuperclass().getName().toString();
}
JFrame frame = (JFrame)frameCheck;
if(actionCommand.equals("Close")) {
frame.dispose();
}
}
}
The above code will work for any button that is a child at any level of a class which extends JFrame. Obviously if your object just is a JFrame it's just a matter of checking that class directly rather than checking the super class.
上面的代码适用于作为扩展 JFrame 的类的任何级别的子项的任何按钮。显然,如果您的对象只是一个 JFrame,则只需直接检查该类而不是检查超类。
Ultimately using this method you're getting a reference to something like this: MainClass$CloseWindow which has the super class JFrame and then you're casting that reference to JFrame and disposing of it.
最终使用此方法您将获得对类似以下内容的引用: MainClass$CloseWindow 具有超类 JFrame 然后您将该引用转换为 JFrame 并处理它。
回答by Ishtar
[Remark by chiccodoro: An explanation why/when leaking this
can cause issues, even if the leaking statement is placed last in the constructor:]
[chiccodoro 的评论:解释为什么/何时泄漏this
会导致问题,即使泄漏语句放在构造函数的最后:]
Final field semantics is different from 'normal' field semantics. An example,
最终字段语义不同于“正常”字段语义。一个例子,
We play a network game. Lets make a Game object retrieving data from the network and a Player object that Listens to events from the game to act accordingly. The game object hides all the network details, the player is only interested in events:
我们玩网络游戏。让我们创建一个从网络中检索数据的 Game 对象和一个侦听游戏事件以采取相应行动的 Player 对象。游戏对象隐藏了所有网络细节,玩家只对事件感兴趣:
import java.util.*;
import java.util.concurrent.Executors;
public class FinalSemantics {
public interface Listener {
public void someEvent();
}
public static class Player implements Listener {
final String name;
public Player(Game game) {
name = "Player "+System.currentTimeMillis();
game.addListener(this);//Warning leaking 'this'!
}
@Override
public void someEvent() {
System.out.println(name+" sees event!");
}
}
public static class Game {
private List<Listener> listeners;
public Game() {
listeners = new ArrayList<Listener>();
}
public void start() {
Executors.newFixedThreadPool(1).execute(new Runnable(){
@Override
public void run() {
for(;;) {
try {
//Listen to game server over network
Thread.sleep(1000); //<- think blocking read
synchronized (Game.this) {
for (Listener l : listeners) {
l.someEvent();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
}
public synchronized void addListener(Listener l) {
listeners.add(l);
}
}
public static void main(String[] args) throws InterruptedException {
Game game = new Game();
game.start();
Thread.sleep(1000);
//Someone joins the game
new Player(game);
}
}
//Code runs, won't terminate and will probably never show the flaw.
Seems all good: access to the list is correctly synchronized. The flaw is that this example leaks the Player.this to Game, which is running a thread.
看起来一切都很好:对列表的访问是正确同步的。缺陷是该示例将 Player.this 泄漏给了运行线程的 Game。
Final is quite scary:
决赛相当可怕:
...compilers have a great deal of freedom to move reads of final fields across synchronization barriers...
...编译器有很大的自由来跨同步障碍移动最终字段的读取...
This pretty much defeats all proper synchronizing. But fortunately
这几乎破坏了所有正确的同步。不过还好
A thread that can only see a reference to an object afterthat object has been completelyinitialized is guaranteed to see the correctly initialized values for that object's
final
fields.
只有在对象完全初始化后才能看到对对象的引用的线程可以保证看到该对象
final
字段的正确初始化值。
In the example, the constructor writes the objects reference to the list. (And thus has not been completely initialized yet, since the constructor did not finish.) After the write, the constructor is still not done. It just has to return from the constructor, but let's assume it hasn't yet. Now the executor could do its job and broadcast events to all the listeners, including the not yet initialized player object! The final field of the player (name) may not be written, and will result in printing null sees event!
.
在示例中,构造函数将对象引用写入列表。(因此还没有完全初始化,因为构造函数没有完成。)写完之后,构造函数仍然没有完成。它只需要从构造函数返回,但我们假设它还没有。现在执行器可以完成它的工作并向所有侦听器广播事件,包括尚未初始化的播放器对象!玩家(姓名)的最后一个字段可能不会被写入,并且会导致打印null sees event!
。
回答by user7868
Wrap your this
in double brackets. Netbeans ignores some errors by default if they are in sub-statements.
this
用双括号包裹你的。如果某些错误在子语句中,Netbeans 默认会忽略它们。
public MyClass() {
...
instances.add((this));
}