什么是“逃逸”警告,我该如何处理?

What is a "this-escape" warning, and how do I deal with it?

提问人:davidalayachew 提问时间:9/28/2023 最后编辑:Mark Rotteveeldavidalayachew 更新时间:10/4/2023 访问量:361

问:

我很难找到这方面的资源,然而,当我在最新的 Java (21) 上编译代码时,我的许多类都遇到了这个错误。

下面是一个代码示例。

public class ThisEscapeExample
{

        public Object o;

        public ThisEscapeExample()
        {

                this.overridableMethod();

        }

        public void overridableMethod()
        {

                this.o = new Object();

        }

}

这是我的编译命令。

javac -Xlint:all ThisEscapeExample.java
ThisEscapeExample.java:9: warning: [this-escape] possible 'this' escape before subclass is fully initialized
                this.overridableMethod();
                                      ^
1 warning
java 范围 编译器警告 this-pointer

评论

0赞 davidalayachew 9/28/2023
有人可以帮我处理标签吗?我觉得那里还有改进的余地,但我想不出什么更合适。
1赞 Mark Rotteveel 9/28/2023
请提供最小的可重现示例和完整的编译器警告/错误
1赞 DuncG 9/28/2023
昨天关于这个问题的问题
0赞 davidalayachew 9/29/2023
@MarkRotteveel我已经这样做了并编辑了问题。
1赞 davidalayachew 9/29/2023
@DuncG 这当然是同一个源问题。不过,我认为我的问题也很有用,因为它显示了人们与此警告的更常见的互动。
2赞 davidalayachew 9/29/2023
@user85421 如果你暗示对我的问题提出批评,我愿意听听。但是,我不理解您的评论。

答:

5赞 davidalayachew 9/28/2023 #1

以下是引入此新警告的 JDK Bug System 条目 - https://bugs.openjdk.org/browse/JDK-8299995

长话短说,警告是警告你,当一个子类可能能够使用一个在超类的构造函数中也被调用的方法时。this-escape@Override

这很危险,因为重写构造函数中使用的方法允许子类在子类初始化期间无意中引入错误。如果该方法依赖于尚未创建的状态,因为我们仍在超级构造函数中怎么办?毕竟,在调用超级构造函数之前,您不能在子类的构造函数中执行任何操作(目前)。

有几种方法可以解决这个问题。

  1. 在构造函数中仅使用无法重写的方法。

    • static方法。

    • final方法。

    • private方法。

  2. 使类本身 .final

  3. 不要一开始就传入/使用,而是传入你需要的特定组件。thisthis

    • 基本上,停止懒惰,明确自己的需求。不要只传入你的上帝对象——只传入你需要的特定属性。

请注意 - 这些规则以递归方式应用。这意味着,当您在构造函数中调用方法时,该方法不仅必须“不可重写”,而且该方法传递到的方法也必须与上述规则之一匹配。如果顶级方法不可重写,但其中某个方法可重写,并且该方法在其作用域内,则编译时将收到错误。下面是一个示例。thisthisthis-escape

import javax.swing.*;

public class GUI
{

   private final JFrame frame;

   public GUI()
   {
   
      this.frame = new JFrame();
   
      this.frame.add(this.createBottomPanel());
   
   }

   //final! Does that mean we are safe?
   final JPanel createBottomPanel()
   {
   
      final JButton save = new JButton();
   
      save
         .addActionListener
         (
            /*
             * No. We get the warning here at the start of this lambda.
             * The reason is because we have given this lambda the
             * ability to call this.toString(), and we don't know when
             * it will do that. Maybe now, maybe later. But if it does
             * it now, then we could end up doing things before the
             * object is fully created. And if that were to happen, then
             * that would be a this-escape. So, the warning occurs here,
             * to let you know that it is possible.
             */
            actionEvent ->
            {
           
               this.toString();
           
            }
           
         )
         ;
   
      return null;
   
   }

}