0

Is it possible to prevent the instantiation of super constructor if a parameter of subclass constructor is illegal?

I know that super should be the first instruction in the subclass constructor, but what happens if I throw an exception? Is the "super" object created anyway?

Example: The subclass must not create the object if y < 10. But if super comes before Y's control, is an object still created?

public class SuperClass(){ 
    public SuperClass(String x) { ... }
}
    

public class SubClass extends SuperClass{
    private int subY;
    
    public SubClass(int y){

        super();
    
        if(y<10){

             this.subY = y;

        }else{
            
             throw new IllegalArgumentException("Error: y must be <10");
        }
    }
}

I read this, but it is not the case of a subclass Preventing instantiation of a class if argument to constructor is illegal?

ceembroo
  • 23
  • 4
  • 2
    What does `class mySuperClass(String x)` mean? That's not valid Java. – Sweeper Oct 23 '22 at 09:41
  • 3
    If the superclass constructor takes parameters, then you actually have a chance, when the parameters are being evaluated. Anyway, I think this is an [XY problem](https://xyproblem.info). Why are you trying to do this? – Sweeper Oct 23 '22 at 09:45
  • 1
    depends on how you define "an object still created". First of all memory is allocated for the whole instance (including the superclasses), then the superclasses are initialized (constructor executed), finally (skipped some steps) the constructor of the actual class is executes and throws an Exception, abruptly exiting the (whole) instance creation. Better explained in the Java Language Specificaiton [12.5. Creation of New Class Instances](https://docs.oracle.com/javase/specs/jls/se19/html/jls-12.html#jls-12.5) – user16320675 Oct 23 '22 at 11:27
  • 1
    At the point *any* constructor code is run, the object has already been created. A constructor in Java is for initialization purposes. As an aside, even if super-constructors are called, there is one and only one object, no matter how many super classes an object has. – Mark Rotteveel Oct 23 '22 at 14:39
  • If the constructor throws, no object is created (or any partly created super class parts are garbage collected). – Robert Oct 23 '22 at 15:37
  • Can you elaborate on why you don't want the superclass constructor to run? – templatetypedef Oct 23 '22 at 19:32
  • @templatetypedef just because I think it may not be correct to execute the super constructor if the subclass constructor cannot be executed due to an incorrect parameter. Or at least, that's what I'm trying to figure out. But if it is true that garbage collection takes care of cleaning, then it may not be a problem to start the super constructor anyway. – ceembroo Oct 25 '22 at 14:51
  • So, if I understand what user16320675 and @Robert say, it is not a problem to launch the super even if the sub-class constructor fails: the exception takes care of it. Thank You all – ceembroo Oct 25 '22 at 15:03

1 Answers1

2

One option is to use a factory method, which lets you do something and then have the constructor run:

public class subClass extends mySuperClass {
    private subClass(int y) { // <-- Note: Now private
        super(/* ... args ... */);
        this.y = y;
    }

    public static subClass create(int y) {
        if (y >= 10) {
             throw new IllegalArgumentException("Shame! Shame!");
        }
        return new subClass(y);
    }
}

Now, you'd create objects by writing subClass.create(arg); rather than new subClass(arg);

A second option would be to replace the int parameter with a parameter of a custom type representing what you want. This gives more fine-grained control and makes clear that the argument isn't an int per se, but rather some sort of limited value.

public class subClass extends mySuperClass {
    public static final class SpecialInt {
        private final int value;
        
        SpecialInt(int val) {
            if (val < 10) {
                throw new IllegalArgumentException("That int is Not Special.");
            }
            value = val;
        }
    }

    public subClass(SpecialInt y) {
        super(/* ... args ... */);
        this.y = y.value;
    }
}

I like this option because it makes clear that there's a restricted type of items that can be given to the constructor, though it does add a bit more syntax (new subClass(new subClass.SpecialInt(137)) versus newSubclass(137)).

A final, hacky, "not great but it'll work" option would be to add in static method that checks the argument to the subclass constructor and, in the normal case, returns the proper argument to the superclass constructor. However, if the argument is invalid, the helper throws an exception. This assumes that the superclass constructor takes at least one argument and won't work otherwise.

public class subClass extends mySuperClass {
    private static final String argCheck(int y) {
        if (y >= 10) {
            throw new IllegalArgumentException("You have committed a Programming Sin.");
        }
        return /* whatever you would normally send to the superclass. */;
    }

    public subClass(int y) {
        super(argCheck(y)); // Check y before invoking the superclass ctor

        this.y = y;
    }
}
templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • 1
    Good advice! To the OP, it's still worth considering why this is important. If calling the superclass constructor has some kind of side-effect that is undesirable when the subclass constructor fails, this should be a red flag and prompt a redesign of the superclass. Constructors should avoid side-effects for this very reason. – Tim Moore Oct 24 '22 at 00:49
  • @Sweeper sorry, my fault. I fixed it. Thank you – ceembroo Oct 24 '22 at 11:49
  • @templatetypedef thank you very much, these solutions are brilliant – ceembroo Oct 25 '22 at 14:58