Preventing "this" exit during construction with finite fields

public class App { private final A a; private final Server server; public App(){ a = new A(this); //Bad, this is escaping before it initialized. } @Subscribe //This event fires some time after App is finished constructing. public void registerStuff(RegisterEvent event){ server = event.getServer(); //Not possible due to final field and this not being the constructor, is there such thing as a lazy final? a.register(); } } public class A { private final App app; private final BuilderSpec spec; public A(App app){ this.app = app; this.spec = app.getServer().builder(this.app).build(); } public void register(){ app.getServer().doStuff(this.app, this.spec); } } 

I got a little acquainted with what happens with the "this" escaping, and we understand that the previous code is bad, since I understand little what external processes do with this link, so it should not be passed outside the constructor until it is built.

However, due to the last fields in the application and A, I really don't see how I can initialize this later or lazily. Does the margin make the final less important than it eludes? I'm not sure.

+6
source share
4 answers

Constructor dependencies can become a problem if they change cyclically. Most IoC containers work with post-construct methods to avoid problems with creating objects.

Applying a principal to your code will look like

 class App { private A a; public App() { // not necessary to have, just to show that we have no logic inside } protected void init() { // choose access modifiers // init something in App; a = new A(this); // safe to pass this now. } } class A { private final App app; public A(App app) { this.app = app; // do whatever you need // if logic affects other components, create init() method here } } 

Yes, the link in the application is not final. But this design is more natural and less error prone.

+2
source

As mentioned in @Boris_the_Spider, passing this in the constructor is not necessarily bad if you make sure your class is in a consistent state (see Passing β€œthis” to the java constructor ). However, be careful with the continuation of the App in the future, because in this case everything can go very badly (see Do not publish the "this" link during construction )

However, another valid option (as you mentioned) is to remove final from A In this case, I would recommend declaring a getter method. Since A is private, access to it will be available only to the App class, so you just need to make sure that the App uses the getA() method whenever you need access to A :

 public class App { private A a; private final Server server; public App(){} // Define getter private A getA(){ return a == null ? new A(this) : a; } @Subscribe public void registerStuff(RegisterEvent event){ server = event.getServer(); // Use getter getA().register(); } } 
+1
source

Your code does not compile because you are not initializing the Server server variable, which is final . In any case, you can pass the Provider , which should provide the App your A , when it becomes available (fully initialized). Your code might look like this:

 public App(){ appProvider = new Provider<App>() { private volatile App app; // setter here @Override public String get() { return app; } }; a = new A(appProvider); } @Subscribe public void registerStuff(RegisterEvent event){ // set server to your App appProvider.set(this); } 

then you can check A whether the provider returns get null or not. I believe the javax.inject package contains the appropriate Provider interface for you.

The registerStuff method is great for providing the App , because at this point it is fully initialized. This also answers your question about lazy initialization.

+1
source

Do you even need an App class? A only Server is required. Just move registerStuff() from App to A and use only A

 public class A{ @Subscribe public void registerStuff(RegisterEvent event){ Server server = event.getServer(); BuilderSpec spec = server.builder(this) // not sure what `builder()` needs but might only be A ? .build(); server.doStuff(this, spec); } } 
+1
source

Source: https://habr.com/ru/post/989018/


All Articles