2

I'm new Java and are currently reading a course at university. We're using Java programming early objects as course literature. I have a question about constructors for new objects. The book states very clearly "Even though it’s possible to do so, do not call methods from constructors."

Is this for all methods, period? I understand the problem with calling the class own instance method from constructor. But helper methods, etc?

I made up a small example.

Here is an example of a class that needs one integer in its constructor. The integer needs to be greater than one.

Would this be acceptable? If not, I guess you can't use any of Java Math util function or anything like that in the constructor?

public class TestClass {

    private int number;

    // Only allow construction if number is greater than one
    TestClass(int number) {
        if (NumberUtils.isGreaterThanOne(number)) {
            this.number = number;
        } else {
            throw new IllegalArgumentException("Number must be above 1");
        }
    }
}
public class NumberUtils {
    
    // Helper method to check if number is greater than one
    public static boolean isGreaterThanOne(int number) {
        return number > 1;
    }
}
MC Emperor
  • 22,334
  • 15
  • 80
  • 130
Speedbrake88
  • 39
  • 1
  • 5
  • 3
    Your code is fine. Anything else? – Tim Biegeleisen Nov 23 '21 at 08:24
  • 4
    This code is totally legit and makes perfectly sense when you want to try to fail fast. I.e. you don't want to be able to create a `TestClass` object with an invalid number, so throwing this exception (and call the check method before) is right and good. – jmizv Nov 23 '21 at 08:25
  • 8
    *"Even though it’s possible to do so, do not call methods from constructors."* — **I disagree.** It's perfectly fine to call methods from constructors. Only calling *overridable* methods of the instance you're currently constructing will cause problems, as it'll leak half-constructed objects. [See here for more info](https://stackoverflow.com/questions/3404301/whats-wrong-with-overridable-method-calls-in-constructors) – MC Emperor Nov 23 '21 at 08:25
  • 3
    *"do not call methods from constructors"* is incorrect, at least incomplete - why should calling methods of other instances or classes be wrong? Calling methods of the actually being initialized instance (`this`) or even passing that instance as an argument to another method can be a problem, since the instance is eventually not fully initialized at that moment. – user16320675 Nov 23 '21 at 08:26

3 Answers3

4

Even though it's possible to do so, do not call methods from constructors.

Well, as I already said in the comments, it's perfectly fine to call methods from constructors. Either the author doesn't really understand it all, or this sentence is poorly-worded.

  • Calling overridable methods of the instance you're currently constructing will cause problems, as it'll leak half-constructed objects. See here for more info. But it looks like you're already aware of this.

  • Also, if you pass this to another method from within a constructor, then you're passing the half-constructed instance. Bad. Don't do it.

But for all other cases, calling another method will cause no problem at all. When you call a method, it is executed, and when it returns, control is passed back to the constructor, which happily continues with constructing the object.

There is absolutely no problem with your code:

TestClass(int number) {
    if (NumberUtils.isGreaterThanOne(number)) {
        this.number = number;
    } else {
        throw new IllegalArgumentException("Number must be above 1");
    }
}

If it were bad to call other methods from the constructor, you had to implement the check employed by isGreaterThanOne all here in the constructor itself. But instead, you happily delegate this to a separate method, which can be separately tested. So you're actually validating the input, which should be done.


Another way to write the validation, is to write guard clauses:

TestClass(int number) {
    // This check here immediately throws an exception if number has an
    // invalid value. This is called a a guard clause, and are usually
    // positioned at the top of the method or constructor.
    if (!NumberUtils.isGreaterThanOne(number)) {
        throw new IllegalArgumentException("Number must be above 1");
    }

    // Continue with normal operation
}

But this is just another way of writing the same logic.

MC Emperor
  • 22,334
  • 15
  • 80
  • 130
1

There are many good reasons to call other methods from constructors. The most common type of call is to validate input values as in your suggestion.

Many classes will use Objects.requireNonNull(xyz); to perform basic validation of each method (or constructor) argument, and perhaps you need to make calls to other static methods to say convert arguments to internal stored values, for range checks, or to store values independent of the caller supplied values.

DuncG
  • 12,137
  • 2
  • 21
  • 33
0

In this case number is not final, maybe you could create a setter and check the condition there.

Something like:

public class TestClass {
    private int number;

    TestClass(int number) {
       setNumber(number)
    }

    public void setNumber(int number) {
        if (number > 1) this.accountId = accountId;
    }

}