Enum
public enum EmployeeStatus {
ACTIVE, IN_ACTIVE
}
In callers scattered all over the application whicg get and set the enum like below. Here are examples
Caller_1
if(employee.getStatus() == EmployeeStatus.STATUS.SUBMITTED) {
}
Caller_2
employee.setStatus(EmployeeStatus.STATUS.SUBMITTED)
Problem
I need to implement the internationalization so that end user sees the employee status as per locale. So when i set the status for french locale
it should set the value from right resource bundel. Is there a way i can achieve this without changing the caller code. Here is the solution
I can think of
My proposed solution :-
public enum EmployeeStatus {
ACTIVE, IN_ACTIVE
public static String toString() {
return I18n.getMessage("label." + this);
}
}
public final class I18n {
private I18n() {
}
private static ResourceBundle bundle;
public static String getMessage(String key) {
if(bundle == null) {
bundle = ResourceBundle.getBundle("path.to.i18n.messages");
}
return bundle.getString(LocaleContextHolder.getLocale());
}
}
With this approach I need to add toString method in every Enum without change in caller ? Is there a better generic approach spring provides ?
I am using spring 4. See if spring can help here .
Related
I have a complex enum class in my spring boot application which holds different status values for different systems.
package com.foo;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public enum Status {
FOO_STATUS("Status1" ,"status_1", "STATUS_1", "stat1"),
BAR_STATUS("Status2" ,"status_2", "STATUS_2", "stat2" ),
FOO1_STATUS("Status3" ,"status_3", "STATUS_3", "stat3" ),
BAR1_STATUS("Status4" ,"status_4", "STATUS_4", "stat4" ),
....
....
....
private final String system1Status;
private final String system2Status;
private final String system3Status;
private final String system4Status;
private static Map<String, String> statusMap;
Status(String system1Status, String system2Status, String system3Status, String system4Status) {
this.system1Status = system1Status;
this.system2Status = system2Status;
this.system3Status = system3Status;
this.system4Status = system4Status;
}
public String getSystem1Status() {
return system1Status;
}
public String getSystem2Status() {
return system2Status;
}
public String getSystem3Status() {
return system3Status;
}
public String getSystem4Status() {
return system4Status;
}
private static void initializeMapping() {
statusMap = new HashMap<>();
for (Status map : Status.values()) {
statusMap.put(map.getSystem1Status(), map.getSystem2Status());
}
}
public static String getSystem2StatusForSytem1Status(String status) {
if (statusMap == null) {
initializeMapping();
}
if (statusMap.containsKey(status)) {
return statusMap.get(status);
}
return null;
}
public static String getSystem3StatusForSytem1Status(String status) {
....
}
public static String getSystem4StatusForSytem2Status(String status) {
....
}
public static String getSystem3StatusForSytem2Status(String status) {
....
}
....
....
}
The enum holds status string mapping for various systems. It also has methods to get different system status by supplying the current system status.
Ex: We can get System1 status by sending the System 2 status value.
As the enum is getting more complex , is there any alternate way to hold this static data?
PS: I know this can be moved to a reference table in DB, But I am looking for any alternate within the code (like loading from yaml file).
The concern about the enum getting more and more complex is only valid if that complexity is accidental, not inherent. Otherwise, switching to a different approach would just move that complexity elsewhere (which kind of seems to be the case in your example). I think it makes sense to keep the enum (even if it grows complex) iif the following conditions are met:
There is no reasonable scenario in which you would want/need to account for new statuses or new mappings (or drop existing ones) without changing the code.
You rely on at least some enum features available out of the box, so you would have to reimplement those by hand. E.g. values() listed in a determinate order, valueOf() used with canonical String labels, ordinal() to infer position, compareTo(), name(), serialization, etc.
You use the enum constants polymorphically (and maybe you need to alter the behavior for some of them without a full-fledged class hierarchy) or you want to leverage the compiler check for exhaustive case branches in switch expressions (with newer java versions).
Say I have a class:
public enum Enums
{
// about thousand different Enums
}
and I have another class where a user signs in, and based on whether or not hes an admin or a regular user, the user has access to a limited list of enums. I know I can get the full list of all the enums from the class, but what is an elegant way to filter these by some criteria, without the Enums class knowing about user information?
Edit:
Here is a snip of what it looks like today:
#GET
#RolesAllowed({ADMIN})
#Path("/test")
public Response reply(#Auth User user)
{
JSONObject jsonObject = new JSONObject();
jsonObject.put("enums", Arrays.toString(Enums.values()));
return Response.ok(jsonObject.toJSONString()).build();
}
I am returning the full list of 1000+ events, when admin should only see a limited amount.
Let us take example of a week days name as enum. See below
public enum DaysOfWeekEnum {
SUNDAY("off"),
MONDAY("working"),
TUESDAY("working"),
WEDNESDAY("working"),
THURSDAY("working"),
FRIDAY("working"),
SATURDAY("off");
private String typeOfDay;
DaysOfWeekEnum(String typeOfDay) {
this.typeOfDay = typeOfDay;
}
// standard getters and setters
public static Stream<DaysOfWeekEnum> stream() {
return Stream.Of(DaysOfWeekEnum.values());
}
}
Now we will write an example in order to print the non-working days:
public class EnumStreamExample {
public static void main() {
DaysOfWeekEnum.stream()
.filter(d -> d.getTypeOfDay().equals("off"))
.forEach(System.out::println);
}
}
The output we get when we run this:
SUNDAY
SATURDAY
Sorry m on phone, please format code in answer.
Let us say your enum looks like
private enum Enums {
A,
B,
C,
D
}
where A, B are admin specific.
Create a class that allows access to the Enums based on whether the person is admin or not.
public class SO {
EnumSet<Enums> adminEnums = EnumSet.allOf(Enums.class);
EnumSet<Enums> nonAdminEnums = EnumSet.of(Enums.C, Enums.D);
public Set<Enums> getEnums(User user) {
boolean isAdmin = user.isAdmin(); //An example
return isAdmin ? adminEnums : nonAdminEnums;
}
An EnumSet is a special Set implementation optimized for storing a set of enums. adminEnums stores the list of all enums whereas nonAdminEnums has the limited set of enums.
You either have to explicitly specify the list of restricted enums for admin or specify the open enums and derive the other based on this. Not only this is tedious but error-prone. In the future, when you add a new enum instance, you have to update this too and it is easy to forget this.
It would be better if the enums itself contained this information like,
private enum Enums {
A(false),
B(false),
C(true),
D(true);
private boolean adminSpecific;
Enums(boolean adminSpecific) {
this.adminSpecific = adminSpecific;
}
public boolean isAdminSpecific() {
return adminSpecific;
}
}
In this case, we can derive the list based on the information contained in the enum instance.
Set<Enums> adminEnums = Arrays.stream(Enums.values())
.filter(Enums::isAdminSpecific)
.collect(Collectors.toSet());
Say I have a function that looks at a file and returns two results: recognized and unrecognized. When it returns the recognized result, I want the result to also contain a message but when it is unrecognized, no message is necessary.
public Result checkFile(File file) {
...
}
There are two ways I can think of to accomplish this...
Have the Result class like so:
class Result {
private Type type;
private String message;
enum Type {
RECOGNIZED, UNRECOGNIZED
}
}
Or do it like so:
class Result {
}
class Unrecognized extends Result {
}
class Recognized extends Result {
private String message;
}
I'm inclined to use the second method, even though I'd have to check the result using instanceof and I've read that instanceof should be avoided whenever possible, but doing this avoids having a null message when the result is unrecognized. For this example a null message wouldn't be much of an issue, but what if there is a lot more data associated with a recognized result? It seems like worse practice to me to instantiate a class that could have all null fields.
What is the best practice to handle this situation? Is there some standard method or pattern?
Two classes might be overkill, because of it being one and the same class of object. Also an enum with two values which merely reassemble true and false is not required. One class Result should suffice and this would also remove the demand for a common interface. I'd be all for "no complexity beyond necessary" ...
class RecognitionResult {
private String message = "default message";
private boolean recognized = false;
public Result() {}
public Result(boolean value) {
this.setRecognised(value);
}
public boolean setRecognised(boolean value) {
this.recognized = value;
}
public boolean setMessage(#NonNull String value) {
this.message = value;
}
public boolean getRecognised() {
return this.recognized;
}
#Nullable
public String getMessage() {
return this.recognized ? this.message : null;
}
}
then one can simply do:
return new RecognitionResult(true);
an interface for asynchronous callbacks might look alike this:
interface Recognition {
void OnComplete(RecognitionResult result);
}
or if you really want to optimize:
interface Recognition {
void OnSuccess(RecognitionResult result);
void OnFailure(RecognitionException e);
}
Of course there's no 'correct' design here - it's going to be a matter of opinion which way you go. However my view is that the modern trend in OOD is to minimise the use of extension and to use delegation and implementation of interfaces wherever possible.
As a general rule, whenever you think of using instanceof, reconsider your design.
This would be my suggestion:
interface Result {
boolean isRecognised();
String getMessage();
}
class RecognisedResult implements Result {
private final String message;
public boolean isRecognised() {
return true;
}
public String getMessage() {
return message;
}
}
class UnrecognisedResult implements Result {
public boolean isRecognised() {
return false;
}
public String getMessage() {
throw new UnsupportedOperationException("No message for unrecognised results");
}
}
you can look at the way Retrofit implement your concept of "recognised" and "message"
https://square.github.io/retrofit/2.x/retrofit/retrofit2/Response.html. it is similar to your first method.
what they did is to have a class called Response, containing a method called isSuccessful(), and a method called body() containing the payload if it's successful (or null if it is unsuccessful.
you can try some thing like the following
class Result {
private Type type;
private String message;
public bool isSuccessful(){
return type == RECOGNIZED;
}
public String getMessage(){
return message; //null if unrecognized.
}
enum Type {
RECOGNIZED, UNRECOGNIZED
}
}
The functional way to do this would be to use an Either type, which doesn’t come with the JDK, but is available in vavr library. Based on your comments on this thread, it appears you don’t clearly understand how type inheritance works. In that case, a functional solution may be overkill, and I’d suggest going with #sprinter’s solution.
In one of my projects I need to compare the URI with several regex patterns(15+ regex patterns). Currently I have used a if ladder to see if either one of them gets matched and there onward the logical part of the code is executed.
Glimpse of the code now:
if (uri.matches(Constants.GET_ALL_APIS_STORE_REGEX)) {
long lastUpdatedTime = InBoundETagManager.apisGet(null, null, tenantDomain, null);
String eTag = ETagGenerator.getETag(lastUpdatedTime);
if (eTag.equals(ifNoneMatch)) {
message.getExchange().put("ETag", eTag);
generate304NotModifiedResponse(message);
}
message.getExchange().put("ETag", eTag);
}
else if (uri.matches(Constants.GET_API_FOR_ID_REGEX)) { // /apis/{apiId}
apiId = UUIDList.get(0);
String requestedTenantDomain = RestApiUtil.getRequestedTenantDomain(tenantDomain);
long lastUpdatedTime = InBoundETagManager.apisApiIdGet(apiId, requestedTenantDomain, uri);
String eTag = ETagGenerator.getETag(lastUpdatedTime);
handleInterceptorResponse(message, ifNoneMatch, eTag);
}
else if (uri.matches(Constants.GET_SWAGGER_FOR_API_ID_REGEX)) { // /apis/{apiId}/swagger
apiId = UUIDList.get(0);
long lastUpdatedTime = InBoundETagManager.apisApiIdSwaggerGet(apiId, tenantDomain);
String eTag = ETagGenerator.getETag(lastUpdatedTime);
if (lastUpdatedTime == 0L) {
log.info("No last updated time available for the desired API swagger json file");
}
handleInterceptorResponse(message, ifNoneMatch, eTag);
}
Can someone please introduce me with a more neat and clever way of doing this regex matching thing?
One url-type(regex) = one handler = one class. This way would be much easier to read and support especially if you have 15 regex checks.
interface URLHandler {
void handle();
boolean isSupported(String url);
}
class GetAllApisStoreHandler implements URLHandler{
private static final Pattern GET_ALL_API_STORE_PATTERN = Pattern.compile(GET_ALL_APIS_STORE_REGEX);
public boolean isSupported(String url) {
return GET_ALL_API_STORE_PATTERN.matcher(url).matches();
}
public void handle(...) {
...
}
}
class GetApiIdHandler implements URLHandler{
private static final Pattern GET_API_ID_REGEX = Pattern.compile(GET_API_ID_REGEX);
public boolean isSupported(String url) {
return GET_API_ID_PATTERN.matcher(url).matches();
}
public void handle(...) {
...
}
}
class GetApiIdHandler implements URLHandler{
private static final Pattern GET_SWAGGER_FORAPI_ID_PATTERN = Pattern.compile(GET_SWAGGER_FOR_API_ID_REGEX);
public boolean isSupported(String url) {
return GET_SWAGGER_FORAPI_ID_PATTERN.matcher(url).matches();
}
public void handle(...) {
...
}
}
class Main {
private List<URLHandler> urlHandlers;
public void method(){
...
for (URLHandler handler : urlHandlers) {
if(handler.isSupported(url)) {
handler.handle(arg1, arg2, arg3, ...);
}
}
...
}
}
Using multiple classes as #KonstantinLabun proposed is probably the way to go(*), but it shouldn't lead to much code duplication. So use an abstract class instead of (or in addition to an interface). Or (mis)use default methods.
abstract class URLHandler {
abstract void handle();
abstract Pattern urlPattern():
final boolean isSupported(String url) {
return urlPattern().matcher(url).matches();
}
}
class GetAllApisStoreHandler extends URLHandler{
private static final Pattern URL_PATTERN =
Pattern.compile(Constants.GET_ALL_APIS_STORE_REGEX);
Pattern urlPattern() {
return URL_PATTERN;
}
public void handle(...) {
...
}
}
There's no need to invent names for the PATTERN as its scope identified it already. The static field exists only as an optimization, so that the Pattern don't get compiled for each match.
(*) There's nothing wrong with a single class, as long as it's concise (I like spaghetti except in code) and doesn't leak implementation details. There's nothing wrong with multiple classes (except maybe on Android as 50 kB per class might matter) as long as they don't lead to code bloat. An enum is sometimes a good solution, too.
Explanation of abstract class vs. interface
An interface forces you to implement its methods(**), which may quickly lead to duplication. It's advantage is multiple inheritance and some conceptual purity.
An abstract class allows you to gather the common parts. But there's no dilemma, you can do both, see e.g., interface List and abstract class AbstractList.
(**) Since Java 8, an interface can have default methods, so this is no more true. Assuming you want to use them for this purpose. It can't declare any state, but it can access the state of the object. For example, my above URLHandler could be such an interface. There are still disadvantages, e.g., methods must be public and mustn't be final.
G'day, errata ... My plan was as shown below. This update is to clarify and apologise for a late night question. The compile error was due to a problem elsewhere in the file.
Clarification: a simple Java enum, like this:
public enum ServiceSource
{
NONE,
URL,
FILE;
}
Want to checking like, isURL():
public boolean isURL(){
return (URL == this);
}
This works (and compiles) ... There's no question -- Correctly answered by: dasblinkenlight and Elliott Frisch. Thank you very much for your time.
see also:
Lookup enum by string value
How to test enum types?
Since this is an instance method, you need to check that this is equal to URL, like this:
public boolean isURL(){
return (URL == this);
}
Demo on ideone.
If you want to have methods that are polymorphic - i.e. exhibit different behaviour for different instances (values) of your enum class, my preference is to override a common method:
public enum ServiceSource {
NONE("no_value"),
URL("url"){
#Override
public boolean isURL() {
return true;
}
},
FILE("file");
private final String val;
private ServiceSource(String val) {
this.val = val;
}
public boolean isURL() {
return false;
}
}
But for methods that check whether this is specific enum value then adding an isXXX method for each constant seems very wasteful. Really, the very reason to use an enum, is so that you can write
if(thing == ServiceSource.URL)
Elsewhere in your code.
If I understand your question, the correct method in your enum is to use this like so,
public enum ServiceSource
{
NONE( "no_value" ),
URL( "url" ),
FILE( "file" );
ServiceSource(String v) {
text =v;
}
private String text;
public boolean isURL() {
return this == URL;
}
}
You can make a method on your Enum to check the value of itself like this:
public boolean isURL(){
return (URL == this);
}
But it's hard to see the value in this approach since every Object has a built in equals() method that accomplishes the same thing.
if (serviceSource.equals(ServiceSource.URL)) { ... }
This would be a more common and obvious way to check the assigned value of an Enum variable (or any variable for that matter). Taking the first approach would require you to have a new isX() method on your Enum; every time you add an Enum constant, you would probably want a new method to accompany it.