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

1

6

I struggle to find resources on this, and yet, so many of my classes are running into this error when I compile my code on the latest Java (21).

Here is a code example.

public class ThisEscapeExample
{

        public Object o;

        public ThisEscapeExample()
        {

                this.overridableMethod();

        }

        public void overridableMethod()
        {

                this.o = new Object();

        }

}

And here is my compilation command.

javac -Xlint:all ThisEscapeExample.java
ThisEscapeExample.java:9: warning: [this-escape] possible 'this' escape before subclass is fully initialized
                this.overridableMethod();
                                      ^
1 warning
Whet answered 28/9, 2023 at 2:30 Comment(6)
Could someone help me with the tags? I feel like there is room for improvement there, but I can't think of what would be a better fit.Whet
Please provide a minimal reproducible example and the full compiler warning/errorBaht
Question on this issue yesterdayTannie
@MarkRotteveel I have done so and edited the question.Whet
@Tannie It is certainly the same source problem. Though, I think my question is useful too since it shows a more common interaction people will have with this warning.Whet
@user85421 If you are implying a criticism of my question, I am open to hearing it. However, I don't understand your comment as is.Whet
W
13

Here is the JDK Bug System entry that introduces this new warning - https://bugs.openjdk.org/browse/JDK-8299995

Long story short, the this-escape warning is to warn you when a subclass may be able to @Override a method that is also called in the superclass' constructor.

This is dangerous because overriding a method that is used in the constructor allows subclass' to unintentionally introduce a bug during a subclass' initialization. What if that method depends on state that has not yet been created because we are still in the super constructor? After all, you cannot do anything in the subclass' constructor before calling the super constructor (for now).

There are a few ways to remedy this.

  1. Only use methods in the constructor that cannot be overridden.

    • static methods.

    • final methods.

      • MUST BE IN THE SAME CLASS AS YOUR CONSTRUCTOR, YOU CANNOT USE PARENT FINAL INSTANCE METHODS IN THE CHILD CLASS' CONSTRUCTOR
    • private methods.

  2. Make the class itself final.

  3. Don't pass in/use this to begin with - instead, pass in the particular component of this that you needed.

    • Basically, quit being lazy and be explicit with what you need. Don't just pass in your God object -- pass in only the specific attributes you need.

Please note - these rules apply recursively. Meaning, when you call a method in the constructor, not only does that method have to be "not-overridable", but the methods that that method passes this into must ALSO match one of the rules above. If your top-level method is not overridable, but one of the methods inside of it is, and that method has this in its scope, then you will receive a this-escape error upon compilation. Here is an example.

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

}

Now, if none of the solutions above are an option for you, consider the tactic of lazy loading your data. Lazy loading is when you load your data only as needed -- meaning, NOT in your constructor. For example, if your class needs a database connection, don't make the connection happen in the constructor, do it in the getter call. Like this.

public class SomeClass
{

    private DbConnection connection = null;

    //More fields here.

    public SomeClass()
    {

        //Don't set the db connection here.

    }

    public DbConnection getConnection()
    {

        if (this.connection == null)
        {

            this.connection = createAConnection(this);

        }

        return this.connection;

    }

}

And finally, if none of this works, or there is just some entirely unescapable situation, there are 2 very hacky, undesirable ways to just silence the error. You really should NOT depend on this, but if you are CERTAIN that it can't hurt you, here are 2 ways to silence it.

  • Supress the warning using @SuppressWarnings("this-escape").
    • But please note, if you do this, then your class will not be allowed to become a Value class. Value classes are going to give you a MASSIVE performance increase. But in order to turn your class into a value class, you have to have no this-escape. That warning turns into an error for value classes, so if you have any this-escape in your value class, then your value class will not compile.
  • Turn off ALL reports of a "this-escape" warning! DANGEROUS! DO NOT DO UNLESS ABSOLUTELY NECESSARY!
    • This is the nuclear option. The big red button. Please self-reflect before doing this. Doing this will turn off ALL reports of a this-escape for ALL SOURCE CODE THAT YOU ARE COMPILING. So that means that you should really only use this if a whole bunch of classes are getting this warning, and you know for certain that none of them are a problem (or that, any problems that exist, you are willing to pay the consequences for them). If that's true, then you can type in -Xlint:-this-escape as one of the command line arguments for your compiler command, and that will turn off this warning entirely for all code that you are compiling. I've said enough about how perilous this is, so I won't repeat myself. I will only add that if you decide to do this, make sure you only do it on the specific files/projects/workspaces you want, and don't leave this setting on for others. You can look up your IDE's specific settings to figure out how to set this on the PROJECT-SPECIFIC level and not the ALL-PROJECTS level.
Whet answered 28/9, 2023 at 2:30 Comment(8)
By the way, if you all see anything wrong with this answer, or it does not solve your problem, please let me know! This answer is getting a lot of traction, so the better my answer is, the better off everyone else is!Whet
I have this problem and the method called (on the superclass) is final!Counterproductive
Post your example. If it is long, ask a new question, and I will edit this answer to show it off as an example.Whet
@AlessandroPolverini And if you do make a new question, send me a link to it.Whet
@AlessandroPolverini Please ignore my previous comments. I see the problem. I need to add more detail to my answer. Please see my edited answer and tell me if that clarifies where the issue is.Whet
"Value classes are going to give you a MASSIVE performance increase." — I think that's yet to be determined.Arrest
@M.Justin Go pull some of the early access builds of Valhalla. They gave us a taste of primitive classes (back when that was the word), and it was already a night and day difference performance wise. They said that they have an even BETTER implementation that they are working on now. So, yes, technically, their current implementation that they are working on might be slow and also end up the final product. But I will assume that they will get within spitting distance of the previous early access, which means my original statement is likely to be correct.Whet
@Whet Nice!Arrest

© 2022 - 2025 — McMap. All rights reserved.