Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Always execute method after constructor in Java

I have a situation where I always need to run a certain bit of code that depends on the object itself

public abstract class A{
    public A(X x){
        //init A stuff
        x.getAList("stuff").add(this);
        x.getAList("otherstuff").add(this);
    }
}

public class B extends A{
    public B(X x){
        super(x);
        //init B stuff
    }
}

public class C extends A{
    public C(X x){
        super(x);
        //init C stuff
        x.getAList("otherstuff").remove(this);
        x.getAList("morestuff").add(this);
    }
}

public class SomeClass{
    private X someX;

    public A somefunc(boolean b){
        if(b){
            return new B(someX);
        }else{
            return new C(someX);
        }
    } 
}

The problem is the following. In this example I use this in the constructor. If another thread tries to access the object through someX.getAList, it could cause that thread to get access to the object before the constructor has ended.

You could make it so that the object gets added to the AList by somefunc

public class SomeClass{
    private X someX;

    public A somefunc(boolean b){
        A a;
        if(b){
            a = new B(someX);
            someX.getAList("stuff").add(a);
            someX.getAList("otherstuff").add(a);
        }else{
            a = new C(someX);
            someX.getAList("stuff").add(a);
            someX.getAList("morestuff").add(a);
        }
        return a;
    } 
}

The problem is that B's and C's could also be instantiated elsewhere and that everytime a B or C is created they would need to be added in that specified way. I don't want adding the object to the AList to be the responsibility of the user, but of the class. I also don't want the user to have to call an init function that does this for them. On the other hand, I don't want any concurrency issues.

Is there a way or a pattern that makes it possible to implement this?

Golang has something like defer that lets you run a piece of code after the function/method/constructor is done.

like image 511
Rik Schaaf Avatar asked May 22 '16 23:05

Rik Schaaf


People also ask

Is it OK to call methods in constructor?

Yes, as mentioned we can call all the members of a class (methods, variables, and constructors) from instance methods or, constructors.

Is constructor called before main method?

For the global object, the constructors are called before entering into the main function.

When constructor is automatically executed?

12.1 Constructors and destructors A constructor is a special initialization function that is automatically called whenever a class is declared.

What is the execution order of constructor in Java?

When a class object is created using constructors, the execution order of constructors is: Constructors of Virtual base classes are executed, in the order that they appear in the base list. Constructors of nonvirtual base classes are executed, in the declaration order.


2 Answers

Make a Factory-Method for the super and subclass instead and make the constructors private, forcing everyone who wants an instance to use the factory method. A factory method is a method that returns a completely constructed instance. Once the instance is completely constructed (after the constructor was called in the factory method) add the instance to the list, that way no thread can get hold of a incomplete/non-finalized instance.

The point of the Factory-Method is to strictly isolate all the initialisation code from any non-initialisation code, to avoid access to and exposure of uninitialised fields. Also it can serve as a selector for users, automatically returning a suitable (sub-)type, without having to be specified.(Interesting design-patterns)

abstract class A{
    protected A(){
        //constructor code goes here
    }
    public void afterFinalisation(final X x) {
        x.getAList("stuff").add(this);
        x.getAList("otherstuff").add(this);
    }
}

class B extends A{
    protected B(){
        super();
        //constructor code goes here
    }
    public static B create(final X x) {
        final B returnValue = new B();
        returnValue.afterFinalisation(x);
        return returnValue;
    }
}

class C extends A{
    protected C(){
        super();
        //constructor code goes here
    }
    @Override
    public void afterFinalisation(final X x) {
        super.afterFinalisation(x);
        x.getAList("otherstuff").remove(this);
        x.getAList("morestuff").add(this);
    }
    public static C create(final X x) {
        final C returnValue = new C();
        returnValue.afterFinalisation(x);
        return returnValue;
    }
}

class SomeClass{
    private final X someX = new X();

    public A somefunc(final boolean b){
        if(b){
            return B.create(this.someX);
        }else{
            return C.create(this.someX);
        }
    }
}

The credit for the constructor code goes to coolcats iteration of my answer, I was trying to avoid putting code into the protected constructors and worked with a init() method instead, which required a big unelegant-workaround for final fields.

like image 191
HopefullyHelpful Avatar answered Sep 27 '22 20:09

HopefullyHelpful


By taking a few design decisions from HopfullyHelpful I end up with liking the following design best:

public abstract class A{

    protected A(X x){
        //constructor with all inits
    }
    protected A publish(X x) {
        x.getAList("stuff").add(this);
        x.getAList("otherstuff").add(this);
        return this;
    }
}

class B extends A{
    protected B(X x){
        super(x);
        //constructor with all inits
    }
    protected B publish(X x) {
        super.publish(x);
        return this;
    }
    public static B create(X x) {
        return new B(x).publish(x);
    }
}

class C extends A{
    protected C(X x){
        super(x);
        //constructor with all inits
    }
    protected void publish(X x) {
        super.publish(x);
        x.getAList("otherstuff").remove(this);
        x.getAList("morestuff").add(this);
        return this;
    }
    public static C create(X x) {
        return new C(x).publish(x);
    }
}

class SomeClass{
    private X someX;

    public A somefunc(boolean b){
        if(b){
            return B.create(this.someX);
        }else{
            return C.create(this.someX);
        }
    }
}
like image 40
Rik Schaaf Avatar answered Sep 27 '22 22:09

Rik Schaaf