Reputation: 755
As we all know it is impossible to extend an enum, for very good reasons. However I have run into a case where I need to add some additional information to each value of an already implemented enum that comes from a separate library, this library is in my control but should not contain information specific to the application I am developing at the moment.
Let's say I use a library that provides a day of week enum like so:
public enum DayOfWeek {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY;
}
Other classes in this external library use this enum
extensively.
Now let's say that we are writing a appointment scheduling application, so we want to somehow tie a closing time to each value of DayOfWeek
, if DayOfWeek
was a part of our application we could just a add a field and a constructor to it but that is not an option here.
DayOfWeek
. Preferably I should be able to pass around an object which can be used as a DayOfWeek
but that also contains a closing time.DayOfWeek
(maybe aliens with an 8 day week invaded or something) then our implementation should fail as early as possible until a corresponding closing time is added to our library. Preferably at compile time.DayOfWeek
for a new developer of our application.My initial thoughts on this was a simple switch statement in a helper method Like as follows:
public static String getClosingTime(DayOfWeek day) {
switch (day) {
case MONDAY:
case TUESDAY:
case WEDNESDAY:
case THURSDAY:
return "17:30";
case FRIDAY:
return "16:00";
case SATURDAY:
case SUNDAY:
return "Closed";
default:
return null;
}
}
(Return values are String
's for simplicity, they should obviously be some kind of object in a real world case).
This succeeds on point 3 to 5 but is burdensome to use as this method has to be called every time a closing time is needed.
Upvotes: 2
Views: 367
Reputation: 1835
If you don't have to use DayOfWeek
directly I would prefer an interface (in the same package
as your enum DayOfWeek
) like
public interface IDayOfWeek {
DayOfWeek getDayOfWeek();
}
and edit your enum
to
public enum DayOfWeek implements IDayOfWeek {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
@Override
public DayOfWeek getDayOfWeek() {
return this;
}
}
Now you're able to craete a new enum
like
public enum DayOfWeekAndClosingTime implements IDayOfWeek {
MONDAY(DayOfWeek.MONDAY, "17:30"),
TUESDAY(DayOfWeek.TUESDAY, "17:30"),
WEDNESDAY(DayOfWeek.WEDNESDAY, "17:30"),
THURSDAY(DayOfWeek.THURSDAY, "17:30"),
FRIDAY(DayOfWeek.FRIDAY, "16:00"),
SATURDAY(DayOfWeek.SATURDAY, "Closed"),
SUNDAY(DayOfWeek.SUNDAY, "Closed");
private final DayOfWeek day;
private final String closingTime;
DayOfWeekAndClosingTime(DayOfWeek day, String closingTime) {
this.day = day;
this.closingTime = closingTime;
}
@Override
public DayOfWeek getDayOfWeek() {
return day;
}
public String getClosingTime() {
return closingTime;
}
}
Now you can treat your additional information as a day of week:
public void handleDayOfWeek(IDayOfWeek day) {
DayOfWeek dayOfWeek = day.getDayOfWeek();
// do something
if (day instanceof DayOfWeekAndClosingTime)
handleClosingTime((DayOfWeekAndClosingTime) day);
}
The negative point is the cross reference between DayOfWeek
and IDayOfWeek
EDIT: to check added values in DayOfWeek
you can add a static block
static {
if (DayOfWeekAndClosingTime.values().length != DayOfWeek.values().length)
throw new IllegalStateException("missing closing times");
}
There you could also check if all day
s of your wrapping enum
are disjunct.
EDIT 2: Thanks to RobertKock
To get a compiler warning you can reduce the constructor by the String
parameter and change it to
DayOfWeekAndClosingTime(DayOfWeek day) {
this.day = day;
String time = "unknown";
switch(day) {
case MONDAY:
case TUESDAY:
case WEDNESDAY:
case THURSDAY:
time = "17:30";
break;
case FRIDAY:
time = "16:00";
break;
case SATURDAY:
case SUNDAY:
time = "closed";
break;
}
this.closingTime = time;
}
Upvotes: 3
Reputation: 43651
Here's a little bit crazy idea. You can extend your enum to accept a visitor.
public interface DayOfWeekVisitor<R> {
public R onMonday();
public R onTuesday();
// ...
}
public enum DayOfWeek {
MONDAY {
public <R> R accept(DayOfWeekVisitor<R> visitor) { return visitor.onMonday(); }
},
// ...
;
public <R> R accept(DayOfWeekVisitor<R> visitor);
}
Then for all of your DayOfWeek
-dependent operations you'll implement specific visitors:
public class ClosingTimeVisitor extends DayOfWeekVisitor<String> {
public String onMonday() { return "17:30"; }
// ...
}
Yes, this extends the enum - but not with any application-specific things.
How does it fit your requirements?
dayOfWeek.visit(ClosingTimeVisitor.INSTANCE)
is, well, OK. Not brilliant but fine.onBumbsday
to your DayOfWeekVisitor
interface. This will immediately force you - per compiler error - to implement this method in all the DayOfWeekVisitor
implementations you have.dayOfWeek.visit(ClosingTimeVisitor.INSTANCE)
or even with some syntactic sugar dayOfWeek.calculate(CLOSING_TIME)
seems quite easy for me. Not the usual thing for starters but I think it's quite easy to understand the concept.Map<DayOfWeek, String>
and likes.Upvotes: 2
Reputation: 847
For these cases, we follow the following implementation.
public enum DayOfWeek {
MONDAY(null),
TUESDAY(null),
WEDNESDAY(null),
THURSDAY("17:30"),
FRIDAY("16:00"),
SATURDAY(null),
SUNDAY("Closed");
private String closingTime;
public String getClosingTime() {
return closingTime;
}
private DayOfWeek(String closingTime) {
this.closingTime = closingTime;
}
}
Here's how to access it.
DayOfWeek.SUNDAY.getClosingTime(); // it returns "Closed"
Upvotes: 0