Reputation: 21
I wrote this class that returns the fiscal year:
public class Fiscal {
private Calendar calendar;
public FiscalDate(String date) {
try {
DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
Date formattedDate = formatter.parse(date);
this.calendarDate = Calendar.getInstance();
this.calendarDate.setTime(formattedDate);
}
catch (ParseException e) {
System.out.print(e);
}
}
}
I'm using it like the following:
String test = new Fiscal("2020-03-31").display();
Everything works fine, I'm just wondering if there is anything wrong with my try and catch in the constructor? Any ways I can improve this class?
Upvotes: 0
Views: 902
Reputation: 439
I would argue against any code in the constructor. You could follow the guidelines in this article: https://www.yegor256.com/2015/05/07/ctors-must-be-code-free.html
Basically initializing your object with the string representation and parsing only at #displayFiscalYear. Any additional behavior can be achieved through decorators, like caching to avoid repeated computes.
It might seem over complicated, but IMHO it's the best approach. If this doesn't convince you though, I would go with a factory approach. Compute the parsing in a factory method and the constructor is directly initialized with a Calendar object.
P.S. I would also recommend to move to java 8's java.time if you can.
Upvotes: 2
Reputation: 86399
LocalDate
from java.time, the modern Java date and time API, for a date.public class FiscalDate {
private LocalDate calendarDate;
}
A LocalDate
is a date in the proleptic Gregorian calendar without time of day. A Calendar
is a date and time of day with time zone, week numbering scheme and more, theoretically in any calendar system. The Calendar
class is also poorly designed and long outdated. So I recommend LocalDate
.
Now which constructors do we want? I’d find it natural to have a constructor that accepts a LocalDate
:
public FiscalDate(LocalDate calendarDate) {
this.calendarDate = calendarDate;
}
If you foresee that the caller will sometimes have the date as a string rather than a LocalDate
, providing a convenience constructor that accepts a string is nice:
/** @throws DateTimeParseException if dateString is not a valid date string in format yyyy-MM-dd */
public FiscalDate(String dateString) {
this(LocalDate.parse(dateString));
}
The this()
call calls the constructor from before. It’s similar to a super()
call, only instead of calling a constructor in the superclass it calls a constructor in the same class. LocalDate.parse()
throws a DateTimeParseException
if the string cannot be parsed into a valid date. It’s an unchecked exception, so we need not declare that the constructor may throw it, but it’s nice for the caller to know, so we put it in the Javadoc comment.
I believe that deciding what to do in case of an invalid string date should be the job of the caller, not this class. So throwing the exception is appropriate (as it is or wrapped in a more appropriate exception type). If you were to catch the exception in the constructor, you would need to initialize the object, and I can’t see how you would be able to do that in any meaningful way.
Upvotes: 0
Reputation: 5244
Yeah so like @Johnny Mopp said in the comments you shouldn't catch the exception silently, instead throw the exception and let the person who is implementing that class choose how to handle it.
public FiscalDate(String date) throws ParseException {
DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
Date formattedDate = formatter.parse(date);
calendarDate = Calendar.getInstance();
calendarDate.setTime(formattedDate);
}
try {
FiscalDate date = new FiscalDate("my date string");
// some code that utilizes date
} catch (ParseException exception) {
// darn, something went wrong, time to handle it!
}
Upvotes: 3