Saturday, 23 September 2017

java - Why cannot I call "this" after I do something in the Constructor





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);
}
}

Answer



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.


No comments:

Post a Comment

casting - Why wasn't Tobey Maguire in The Amazing Spider-Man? - Movies & TV

In the Spider-Man franchise, Tobey Maguire is an outstanding performer as a Spider-Man and also reprised his role in the sequels Spider-Man...