Calculated value in Groovy enum - java

I need to create an enum or a class that has some predefined values and must have an option to calculate other values. Say we have this enum:
enum Duration {
ONE_HOUR("1${DurationUnits.DURATION_SUFFIX_HOUR}"),
ONE_DAY("24${DurationUnits.DURATION_SUFFIX_HOUR}"),
ONE_WEEK("7${DurationUnits.DURATION_SUFFIX_DAY}"),
ONE_MONTH("30${DurationUnits.DURATION_SUFFIX_DAY}")
String duration
Environment(String duration) {
this.duration = duration
}
static String custom(Date startTime, Date endTime) {
// TODO Calculate difference in days between the two dates
}
private static class DurationUnits {
public static final String DURATION_SUFFIX_HOUR = "h"
public static final String DURATION_SUFFIX_DAY = "d"
}
}
So my question is if I can define an enum value, say CUSTOM, that represents the value of all the non predefined ones. That is, a generic value for the custom(Date a, Date b) method.
I can't figure out how to do it, actually I don't think it's even possible.
As an alternative I'm thinking of creating a class instead of an enum, like this:
class Duration {
private static final String DURATION_SUFFIX_HOUR = "h"
private static final String DURATION_SUFFIX_DAY = "d"
/** Predefined values */
static final String ONE_HOUR = "1${DURATION_SUFFIX_HOUR}"
static final String ONE_DAY = "24${DURATION_SUFFIX_HOUR}"
static final String ONE_WEEK = "7${DURATION_SUFFIX_DAY}"
static final String ONE_MONTH = "30${DURATION_SUFFIX_DAY}"
static String custom(Date startDate, Date endDate) {
// TODO Calculate difference in days between the two dates
}
}
What do you think is the best way to go? This duration will be an input parameter to a method that calls a REST web service, that's why I was trying to define it as an enum instead of just passing a string.

You can use String append.
ONE_HOUR("1" + DurationUnits.DURATION_SUFFIX_HOUR),
ONE_DAY("24" + DurationUnits.DURATION_SUFFIX_HOUR),
ONE_WEEK("7" + DurationUnits.DURATION_SUFFIX_DAY),
ONE_MONTH("30" + DurationUnits.DURATION_SUFFIX_DAY);
However it might be better to use TimeUnits
ONE_HOUR(1, TimeUnit.HOUR),
ONE_DAY(1, TimeUnit.DAY),
ONE_WEEK(7, TimeUnit.DAY),
ONE_MONTH(30, TimeUnit.DAY);
Note: not all months have 30 days.
You can also use Duration for a duration of time.
Duration ONE_HOUR = Duration.ofHours(1);
Duration ONE_DAY = Duration.ofDays(1);
This would allow you to create more Duration as needed.

If it were Java (I'm not a Groovy expert) I'd suggest something like ONE_HOUR("1" + DurationUnits.DURATION_SUFFIX_HOUR), or yet better use another enum that represents the unit itself, e.g. like this:
enum DurationUnit {
HOUR ("k"),
DAY( "d" );
private final String key;
//constructor and getter
}
enum Duration {
ONE_HOUR(1, DurationUnit.HOUR ),
...
private final int quantity;
private DurationUnit unit;
//constructor and getters
public String toString() {
return quantity + unit.getKey(); //should return 1h, 24h, 1d etc.
}
}
This should be doable in Groovy as well and would allow for calculations/comparisons without needing to parse a string.
Update:
For custom durations (and if Java 8's own Duration class doesn't fit your needs) you'd make Duration a normal (immutable) class with some constants:
class Duration {
public static final Duration ONE_HOUR = new Duration(1, DurationUnit.HOUR );
...
private final int quantity;
private DurationUnit unit;
//constructor and getters
}

Related

Boolean value remains false after the value of the static field involved changes

In my assignment I have to use an enum to make an EnumSet of elements that fit the criteria given. So, the code needs to be as flexible as possible and allow any criteria that could be applied to the object declared in the enum.
I've been testing things out, my code involves taking the finals of the enum into the static context, so that a boolean can be applied to them, and then looping through each declared object in the enum to see if they fit the criteria. For some reason though, the state of the boolean doesn't change to true, when the value of the static fields fit the criteria.
Here is my code:
public class test {
// enumeration of persons by age and sex
enum Name {
Adam("Male", 17),
Amy("Female", 24),
Boris("Male", 12),
Bella("Female", 16);
final String _sex;
final int _age;
// static variants to "turn" values to the static context
volatile static String sex = "";
volatile static int age = 0;
Name(String sex, int age) {
_sex = sex;
_age = age;
}
}
public static void main(String[] args) {
// creating a set of people older than 17
EnumSet<Name> set = makeSet(Name.age >= 17);
System.out.print(set.toString());
}
static EnumSet<Name> makeSet(boolean query) {
EnumSet<Name> set = EnumSet.noneOf(Name.class);
for (Name element : Name.values()) {
// this is the "turning" of the values to the static context
Name.sex = element._sex;
Name.age = element._age;
// PROBLEM LIES HERE
// the query remains false, even when age is above 17
if (query) {
set.addAll(EnumSet.of(element));
}
}
return set;
}
}
Changing the static fields to volatile doesn't fix the problem either, so I don't think it's a caching issue.
So, why does the boolean not update? Is there a work-around?
The problem you are facing is that the predicate (Name.age >= 17) is checked at the moment of the function call:
When you are calling makeSet(Name.age >= 17), what actually happens is, the predicate returns a boolean, which at the time of checking returns false, and false is therefore parsed into the function.
Answered in comments by #Turing85
The problem occurred because the boolean is being passed by value.
Using a Predicate instead of just a boolean fixed the problem, because the object reference would be passed on instead, allowing the value to change after the makeSet() method was invoked.
Furthermore, this eliminates the need to take the finals of the enum into the static context.
Here is my code now:
public class test {
// enumeration of persons by age and sex
enum Name {
Adam("Male", 17),
Amy("Female", 24),
Boris("Male", 12),
Bella("Female", 16);
final String sex;
final int age;
Name(String sex, int age) {
this.sex = sex;
this.age = age;
}
}
public static void main(String[] args) {
// creating a set of people older than 17
EnumSet<Name> set = makeSet(query -> query.age >= 17);
System.out.print(set.toString());
}
static EnumSet<Name> makeSet(Predicate<Name> query) {
EnumSet<Name> set = EnumSet.noneOf(Name.class);
for (Name element : Name.values()) {
// PROBLEM FIXED
if (query.test(element)) {
set.addAll(EnumSet.of(element));
}
}
return set;
}
}

Is there any way to avoid this "Attribute value must be constant" error?

We have an annotation that we have built which is used to validate input to a method. Something like this:
#InputValidation(paramName = "foo", regularExpression = RegularExpressionConstants.FOO_REG_EX)
Our RegularExpressionConstants class has a load of different string constants, all representing different regular expressions. This class was starting to look very messy, so I started trying to tidy it up with methods that make it a bit easier to read etc. So the class now looks like this:
public static final String FOO_REG_EX = alphanumericWithRange(1, 16);
public static final String BAR_REG_EX = alphanumericWithRange(2,4);
private static String alphanumericWithRange(int lowerLimit, int upperLimit) {
"[a-zA-Z0-9]{" + lowerLimit + "," + upperLimit + "}";
}
The RegularExpressionConstants class compiles, but the annotations are no longer compiling. The error is Attribute value must be constant. After looking at some of the related questions on StackOverflow, I understand why this is happening. I am mainly wondering if there is any way I can achieve the tidiness I want in the constants class without causing this issue? Or am I just going to have to deal with lots of messy constants?
You can use an enum instead of a list of string constants:
#interface InputValidation {
RegularExpressionConstants regularExpression() default
RegularExpressionConstants.FOO_REG_EX;
}
Using an enum allows also moving the name metadata where the pattern is defined
enum RegularExpressionConstants {
FOO_REG_EX("foo", alphanumericWithRange(1, 16)),
BAR_REG_EX("bar", alphanumericWithRange(2,4));
private final String name;
private final String pattern;
private RegularExpression(String name, String pattern) {
this.name = name;
this.pattern = pattern;
}
public String getName() {
return name;
}
public String getPattern() {
return pattern;
}
private static String alphanumericWithRange(int lowerLimit, int upperLimit) {
return "[a-zA-Z0-9]{" + lowerLimit + "," + upperLimit + "}";
}
}
And the annotation can be applied using the enum:
#InputValidation(regularExpression=RegularExpressionConstants.FOO_REG_EX)
Where the annotation is processed, it's enough to just call:
String pattern = field.getAnnotation(InputValidation.class)
.regularExpression()
.getPattern();
Unless you follow ernest's suggestion and refactor the code, you simply can't. The missing part in your case is an enforcement by the JSL (for what a compile time constant is)
initialized with a constant expression
and calling a method is simply not; even if you can tell that this is a constant, the compiler can't and will not.

One instance variable for multiple values

I'm a Java beginner so bear with me
The requirements are only one instance variable, one constructor, and one method is allowed. Is it possible to make description(instance variable, not the constructor) store multiple values for a date(such as year, month and day)? Something like the code below. I can print it fine if there are multiple instance variables but not with one.
import java.util.Date;
public class MyDate {
public Date description; //type might be wrong
public MyDate(int year, int month, int day, String description) {
//not sure what to put
}
public String toString() {
//return d + "/" + m + "/" + y + " " + description;
}
}
There are different way to store your value into one variable.
Store your values in key-value pair using Map.
Store values in simple List or Arrays and retrieve using index.
Make one class include all your required attribute as instant member for that class. Create object of that class set value to
instant variable.
May one this way will help to solved your problem.
Sample Example :
1. Using Map<String,Object> :
public Map<String,Object> description; //type might be wrong
public MyDate(int year, int month, int day, String descriptionTxt) {
description = new HashMap<String, Object>();
description.put("year", year);
description.put("month", month);
description.put("day", day);
description.put("desc", descriptionTxt);
}
2.Using List<Object> :
public List<Object> description; //type might be wrong
public MyDate(int year, int month, int day, String descriptionTxt) {
description = new ArrayList<Object>();
description.add(year);
description.add( month);
description.add(day);
description.add(descriptionTxt);
}
3.Using Class :
class MyClass
{
private int year;
private int month;
private int day;
private String desc;
//Getter and Setter Method
}
//MyDate class
public MyClass description; //type might be wrong
public void MyDate(int year, int month, int day, String descriptionTxt) {
description = new MyClass();
description.setYear(year);
description.setMonth(month);
description.setDay(day);
description.setDesc(descriptionTxt);
}

Java enum fields serialization/deserialization

Intro
I am using Apache Storm (Local mode, not Remote mode) in my Java project and when creating the topology I need to pass an object to one of the bolts
TopologyBuilder builder = new TopologyBuilder();
.....
builder.setBolt("last-bolt", new MyBolt(Classifier.SECONDS)).someGrouping(...);
.....
LocalCluster cluster = new LocalCluster();
cluster.submitTopology("test", conf, builder.createTopology());
The object itself has some non-serializable fields. Instead of subclassing the classes to which those fields belong and making them Serializable I have taken another approach. Since the actual object isn't gonna be changing a lot and it can be enumerated I've decided to make it an enum and pass it like that to bolt's tasks. The good thing about enum is that it is serializable under all costs. This approach works in local mode because (if I understood Storm correctly) there is only one JVM running on my computer and things can't get complicated actually.
Question
If the enum consists of a static final non-serializable field will that field be constructed properly when the enum is deserialized by another process on a different machine or a cluster running multiple JVMs?
The actual enum (static final field is at the end)
public enum Classifier {
SECONDS {
public String classify(String timestamp) {
DateTime dateTime = formatter.parseDateTime(timestamp);
int second = dateTime.getSecondOfMinute();
if (second <= 30) {
return "00 - 30";
} else {
return "30 - 60";
}
}
public int getNumberOfCategories() {
return 2;
}
},
WEEK {
public String classify(String timestamp) {
DateTime dateTime = formatter.parseDateTime(timestamp);
int dayOfWeek = dateTime.getDayOfWeek();
String typeOfDay = (dayOfWeek >= 1 && dayOfWeek <= 5) ? "workday" : "weekend";
int hour = dateTime.getHourOfDay();
String hourInterval = hour + " - " + (hour == 23 ? 0 : hour + 1);
return typeOfDay + " " + hourInterval;
}
public int getNumberOfCategories() {
return 48;
}
};
private static final DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
public abstract String classify(String timestamp);
public abstract int getNumberOfCategories();
}
More details
DateTimeFormatter and DateTime are from org.joda.time package.
All static final fields are initialized when class is loaded. Whatever serialization mechanism is used it will first initialize the static fields and execute static initialization blocks. Note that static fields are not deserialised because we are not deserialising classes but objects (please also refer to this answer https://stackoverflow.com/a/6429497/1937263).
So the answer is yes, the field should be constructed properly.

Dynamic variable names Java

How will I be able to retrieve the value of a variable which has a dynamic name
For Example I have list of constants
public class Constant{
public static final String S_R = "Standard(240)";
public static final String S_W = "Standard(180)";
public static final String L_R = "Large(360)";
public static final String L_W = "Large(280)";
}
Based on database I build a variable name
String varName = "S" + "_" +"R"; // This can be S_R , S_W , L_R or L_W
String varVal = // How do i get value of S_R
Use a normal HashMap with variable names as strings against their values. Or use a EnumMap with enums as key and your value as values. AFAIK, that's the closest you can get when using Java. Sure, you can mess around with reflection but IMO the map approach is much more logical.
You can use a Map<String, String> and locate the value by its key.
Even better, you can have an enum:
public enum Foo {
S_R("Standard", 240),
S_W("Standard", 180),...;
private String type;
private String duration;
// constructor and getters
}
And then call Foo.valueOf(name)
(You can also do this via reflection - Constants.class.getField(fieldName) and then call field.get(null) (null for static). But that's not really a good approach.)
If you really must do this (and it's unlikely), you would have to use the Java "reflection" APIs.

Categories

Resources