0

I have 2 constructors for my Date class. The first one just have 3 int parameter represent month, day and year. And the second one, I provide it in case user give string as one parameter to represent month/day/year.

Since software rule #1: Don't repeat code. I decide to parse the String in second constructor, and use the first constructor to verify whether the date is valid or not. So in the end I call this to provide the same 3 int parameter as the first constructor.

Well, compiler gives me this Error: error: call to this must be first statement in constructor.

I understand that I can have the second constructor this way, but this against software rule #1.

  public Date(String s) {
    String[] strSplit = s.split("/");
    month = Integer.parseInt(strSplit[0]);
    day = Integer.parseInt(strSplit[1]);
    year = Integer.parseInt(strSplit[2]);
    if (isValidDate(month, day, year)) {
      this.month = month;
      this.day = day;
      this.year = year;
    } else {
      System.out.println("Fatal error:  Invalid data.");
      System.exit(0);
    }
  }

Please take a look the 1st and 2nd constructor:

class Date {

  private int month;
  private int day;
  private int year;

  public Date(int month, int day, int year) {
    if (isValidDate(month, day, year)) {
      this.month = month;
      this.day = day;
      this.year = year;
    } else {
      System.out.println("Fatal error:  Invalid data.");
      System.exit(0);
    }
  }

  public Date(String s) {
    String[] strSplit = s.split("/");
    int m = Integer.parseInt(strSplit[0]);
    int d = Integer.parseInt(strSplit[1]);
    int y = Integer.parseInt(strSplit[2]);
    this(m, d, y);
  }
  .
  .
  .
  public static void main(String[] argv) {
    Date d1 = new Date(1, 1, 1);
    System.out.println("Date should be 1/1/1: " + d1);
    d1 = new Date("2/4/2");
    System.out.println("Date should be 2/4/2: " + d1);
  }
}
Patrick
  • 797
  • 5
  • 15
  • 1
    It's simply a Java rule, presumably to ensure that the object is in a valid state early on. – chrylis -cautiouslyoptimistic- Jul 25 '17 at 07:28
  • https://stackoverflow.com/questions/1168345/why-do-this-and-super-have-to-be-the-first-statement-in-a-constructor might be helpful – Akshay Jul 25 '17 at 07:29
  • 1
    To be on the safe side, why not try validating your parsed integers (isValidDate) before passing them to the second constructor. That way the second constructor only sets the value and is sure of their correctness – jaletechs Jul 25 '17 at 07:33
  • Suggestion: Do your date validation in a `final` method, and then both constructors can call that method. – Kevin J. Chase Jul 25 '17 at 07:35

3 Answers3

1

As the comments mentioned, Java syntax forces to call another constructor (in this or in the super class) as the very first statement in any constructor. See JLS §8.8.7. Constructor Body for more information:

The first statement of a constructor body may be an explicit invocation of another constructor of the same class or of the direct superclass (§8.8.7.1).

But let me show you a reasonable way to create your class. Mostly, your requirement is solved with a factory method:

    public final class Date {
        final int year;
        final int month;
        final int day;

        public static Date parse(String dateAsString) {
            String[] strSplit = dateAsString.split("/");
            int m = Integer.parseInt(strSplit[0]);
            int d = Integer.parseInt(strSplit[1]);
            int y = Integer.parseInt(strSplit[2]);
            return new Date(y, m, d);
        }

        public Date(int year, int month, int day) {
            checkValues(year, month, day);
            this.year = year;
            this.month = month;
            this.day = day;
        }

        private static void checkValues(int year, int month, int day) {
            if (!isValidDate(year, month, day))
                throw new IllegalArgumentException("Invalid date values.");
        }

        private static boolean isValidDate(int year, int month, int day) {
            // TODO Validation!
            return true;
        }
    }

Applying the DRY principle on constructors mostly is done by constructor chaining. Imagine, you also have to create constructors for dates that only have a year or a year and month. Provide two new constructors:

public Date(int year) { this(year, 1, 1); }
public Date(int year, int month) { this(year, month, 1); }

Note, that this is not the best way to represent year-only dates. Maybe you could allow the value 0. Then it would become this(year, 0, 0), for example.

Seelenvirtuose
  • 20,273
  • 6
  • 37
  • 66
  • Can you explain the meaning of static and parse in the method definition. public **static** Date **parse** (String dateAsString), especially the parse thing, I cannot compile without parse. – Patrick Jul 25 '17 at 16:37
  • `parse` is the method's name. – Seelenvirtuose Jul 25 '17 at 16:39
  • Yes, I understand that you change this constructor into a method, but as you can see in my main(), I dont even call any method named **parse**, how does compiler know that it need to call this method? – Patrick Jul 25 '17 at 16:45
  • The compiler doesn't know that. You have to actually call it: `Date aDate = Date.parse("...")`. – Seelenvirtuose Jul 25 '17 at 17:05
  • You know what's funny, I didn't call parse in anywhere, but after compile, d1 = new Date("2/4/2"); do call the parse implicitly and print out the correct date. Sounds correct? – Patrick Jul 25 '17 at 17:16
  • I don't know what you mean. Nothing is called _implicitely_. I told you, you have to call it _explicitely_. – Seelenvirtuose Jul 25 '17 at 17:35
1

because it's a rule of the language and it is present in the Java Language Specification: a call to another constructor in the same class (the this(...) part) or to a constructor in the super class (using super(...)) must go in the first line.

It's a way to ensure that the parent's state is initialized before initializing the current object.

For more information, take a look at this post, it explains in detail the situation.

But in your situation you can use this alternative which follows software rule #1 which you have mentioned.

public class Date {

private int month;
private int day;
private int year;

public Date(int month, int day, int year) {
    this(month+"/"+day+"/"+year);
}

public Date(String s) {
    String[] strSplit = s.split("/");
    int m = Integer.parseInt(strSplit[0]);
    int d = Integer.parseInt(strSplit[1]);
    int y = Integer.parseInt(strSplit[2]);
    if (isValidDate(m, d, y)) {
        this.month = m;
        this.day = d;
        this.year = y;
    } else {
        System.out.println("Fatal error:  Invalid data.");
        System.exit(0);
    }
}

For more information, take a look at this post, it explains in detail the situation.

Jarvis42
  • 61
  • 1
  • 7
0

As other answers point out it's a stated rule of the language.

I just want to point that allowing calls to constructors at other points introduces a whole load of complexity and possible overhead that would inevitably lead to bugs.

You can have a (weaker) rule that a constructor must call either super() or this() exactly once during a constructor. That already does away with the convenience of implicit call to the default super constructor (livable) but how do you implement it?

It's formally impossible (the halting problem) to write a compiler that can statically determine if any or exactly one constructor will be called so at the very least there's run-time checking which could result in new exceptions SecondConstructorCallException and NoConstructorCalledException as well as dealing the prospect of (probably) super class objects not being initialised before/at the start of the sub-class constructor.

All that would entail some run-time tracking of whether a constructor had been called.

The alternative (appears to be) open season on calling constructors multiple times (only during construction - please? See note). That really just pushes the problem into the application developers lap who has to not concern themselves whether they're going to call constructors twice but whether any sub-classes are going to do so and the consequences thereof.

Implementation Inheritance already suffers from the problem that it exposes implementation details of the super class to the sub-class but this seems to be even going the other way! Exposing the super class to implementation details of an as-yet-unwritten sub-class.

I would argue that the existing rules where you can specify the super class constructor using super(...) (necessary), redirect a constructor using this(...) and then run additional code on top supports DRY without introducing yet more language semantics.

If you need to construct objects in some even more complex way the answer is always going to be factory methods (or factory/builder classes).

C++ has (in effect) a similar rule but it's bound into the syntax of providing constructor calls following using : before the constructor body:

MyClass(/*...*/) : MyBase(/*...*/) {/*...*/ }

or (C++11 onwards):

MyClass(/*...*/) : this(/*...*/) { /*...*/ }

That syntax makes it a little clearer what is going on but functionally the outcome is very similar. Notice with multiple inheritance the idea of calling base class constructors at the start of the body would be fairly unwieldy.

Note: only during construction? But is that only with the body of the constructor?

Persixty
  • 8,165
  • 2
  • 13
  • 35