I have an Enum and I like to use it as a mapping between a symbolical name (NORMAL, SPICY, HOT) and its associated values (11, 22, 33).
Lets say the program should use the symbols and in the database are the values stored.
public static enum MyEnum
{
NORMAL (11),
SPICY (22),
HOT (33);
private int n;
MyEnum (int n) // must be a private constructor because of Java
{
this.n = n;
}
public static void initFromNumber (int n)
{
// ??? how to do that
}
public int get ()
{
return this.n;
}
};
Now I read from the database and try to create/init the Enum.
How can I do that with an Enum (it is clear to me to do it with a class) and without having a big IF or SWITCH in it? Is there an "elegant" way?
Normally in my applications, I will loop over all of the values and find the matching one.
public static MyEnum getByNumber(int n) {
for (final MyEnum value : values()) {
if (value.n == n) {
return value;
}
}
throw new IllegalArgumentException("No MyEnum found for n: " + n);
}
Enum is a singleton and you can not just initialize it. Most probably you want to get enum value by number. You can do this with stream:
public static void initFromNumber (int n)
MyEnum enumValue = Arrays.stream(MyEnum.values())
.filter(myEnum -> myEnum.get() == n)
.findFirst().orElseThrow();
}
Related
I want the last enum to have a different value in one of the variables:
private enum thing {
thing0(0),
thing1(1),
thing2(2);
int index;
String s;
private thing(int index) {
this.index = index;
s = index == values().length - 1 ? "b" : "a";
}
}
This doesn't work; you can't call values() in the constructor. Is there another way?
In general, don't rely on the declaration order of the enum values. Item 35 in Effective Java 3rd Ed, "Use instance fields instead of ordinals", explains why. (Note that whilst you are using an instance field for s, its value depends on the ordinal.)
If you want a particular value to have a particular property, pass it in as a constructor parameter.
private enum thing {
thing0(0),
thing1(1),
thing2(2, "b");
int index;
String s;
private thing(int index) {
this(index, "a");
}
private thing(int index, String s) {
this.index = index;
this.s = s;
}
}
If you really do want it to be checking for the last value in the enum, an alternative way to do this is with a getter. Initialize a static final field in the enum to be the last value:
// Invokes `values()` twice, but meh, it's only executed once.
private static final thing LAST = values()[values().length-1];
Then check in a getter:
String s() {
return this == LAST ? "b" : "a";
}
There is no need to maintain an index that always matches the ordinal of the enum constant. Further, you can’t rely on the values() array in the constructor as it is supposed to contain the already constructed instances. But to determine, how many constants exist, it is enough to count the associated fields.
public enum Thing {
thing0,
thing1,
thing2;
final String s;
Thing() {
this.s = ordinal() == numConstants() - 1? "b": "a";
}
#Override
public String toString() {
return name() + "(index = " + ordinal() + ", s = " + s + ")";
}
private static int NUM_CONSTANTS;
private static int numConstants() {
int i = NUM_CONSTANTS;
if(i != 0) return i;
for(Field f: Thing.class.getDeclaredFields()) if(f.isEnumConstant()) i++;
NUM_CONSTANTS = i;
return i;
}
}
So System.out.println(EnumSet.allOf(Thing.class)); prints
[thing0(index = 0, s = a), thing1(index = 1, s = a), thing2(index = 2, s = b)]
Note that numConstants() caches the value in NUM_CONSTANTS which is safe as the private method is only invoked within the class initializer. We can’t use a static final variable here, as all custom class initialization will be done after the enum constants have been constructed.
I'm creating a class MDA_EFSM and it has two variable int k and int[] listA and creating setter and getter methods to initialize these two variables. Then I'm calling getter method of MDA_EFSM in another class. The getter method should return recently set value, but it is returning '0'.
public class MDA_EFSM {
int k;
public int listA[] = {0, 1};
public int getK() {
return k;
}
public void setK(int k) {
this.k = k;
}
public int[] getA() {
return listA;
}
}
public class State {
MDA_EFSM mda = new MDA_EFSM();
public void setMda(MDA_EFSM mdaefsm)
{
mda = mdaefsm;
}
public MDA_EFSM getMda() {
return mda;
}
}
public class S0 extends State{
public void Insert_cups(int n){
if (n > 0){
int value = mda.getK();
}
}
}
I am setting value in one class and getting that value from another class. Here is the code snippet of that class:
public class S1 extends State{
public void Insert(int n){
if (n > 0){
mda.setK(n);
}
}
}
I expect the output of recently set value, but getter method is returning '0'
You haven’t set any value. You got default value of int. By the way I cannot see in code you set any value for int.
Each of your class S0 and S1 has an own instance of MDA_EFSM (by the way you should read Java naming convention). You set the Value of k in S1 but read the value of another k in S0. To achieve what you want k hast to bee static.
I have a bean called vulnerability. It is having a column "severity".
private String severity;
Severity can hold string value High,Medium and Low. Now whenever sorting of this bean on the basis of severity column is done it happens alphabetically i.e. High,Low and Medium. But i want the sorting to happen high,medium, low when descending and low, medium,high when ascending.
I was seeing comparator to make this custom sorting but it needs to cover lots of cases. Isn't their any other way?
You can (and should) use an enum - not a String nor a int:
enum Severity {
LOW, MEDIUM, HIGH;
}
Usage:
List<Severity> lst = new ArrayList<Severity>();
lst.add(Severity.MEDIUM);
lst.add(Severity.LOW);
lst.add(Severity.HIGH);
for (Severity s : lst)
System.out.println("s = " + s);
Collections.sort(lst);
System.out.println();
for (Severity s : lst)
System.out.println("s = " + s);
OUTPUT:
s = MEDIUM
s = LOW
s = HIGH
s = LOW
s = MEDIUM
s = HIGH
EDIT
Since the OP says he can't modify the usage of Strings, we can map the strings into a comparable values:
static Map<String, Integer> severities = new HashMap<String, Integer>();
static {
severities.put("LOW",1);
severities.put("MEDIUM",2);
severities.put("HIGH",3);
}
public static void main(String[] args) {
List<String> lst = new ArrayList<String>();
lst.add("MEDIUM");
lst.add("LOW");
lst.add("HIGH");
for (String s : lst)
System.out.println("s = " + s);
Collections.sort(lst, new Comparator<String>() {
public int compare(String a1, String a2) {
Integer v1 = severities.get(a1);
Integer v2 = severities.get(a2);
return v1.compareTo(v2);
}
});
System.out.println();
for (String s : lst)
System.out.println("s = " + s);
}
and if you want to order the items in descending order you can sort and then reverse:
Collections.sort(lst);
Collections.reverse(lst);
There is an implicit compareTo operator defined on enums, which takes their declaration order to mean "smaller than". No additional code is needed.
enum Severity { Low, Medium, High }
Low.compareTo(High); // returns -1
Medium.compareTo(Low); // returns 1
However, note that the names of the enum constants will be those printed by toString() (and therefore visible to users if you echo enums directly) - if you want to use different internal and external names, possibly to uphold code conventions (say, all-caps-constants), then you will need to add an enum constructor and override the enum's toString method to use the passed-in constructor attribute.
If you cannot use enums, and you cannot change your bean
Then build a Comparator for it:
public class SeverityComparator implements Comparator<String> {
private int direction;
public SeverityComparator(boolean reverse) {
this.direction = reverse ? -1 : 1;
}
private int severity(String s) {
if (s.equals("Low")) { // you really should have constants for the values...
return 0;
} else if (s.equals("Medium")) {
return 1;
} else if (s.equals("High")) {
return 2;
} else {
throw new IllegalArgumentException("Not a severity: " + s);
}
}
#Override
public int compareTo(String other) {
return direction * (severity(this) - severity(other));
}
}
Use as
Collections.sort(listOfSeverities, new SeverityComparator(false)); // ascending
Collections.sort(listOfSeverities, new SeverityComparator(true)); // descending
#alfasin answer is correct but i would suggest using guava's Ordering:
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Ordering;
import javax.annotation.Nullable;
import java.util.List;
public class SeveritySortTest {
private static final List<Severity> SEVERITY_LIST = ImmutableList.copyOf(Severity.values());
public static void main(String[] args) {
Ordering<Severity> severityOrdering = Ordering.natural().onResultOf(new Function<Severity, Integer>() {
#Nullable
#Override
public Integer apply(#Nullable Severity input) {
return input.getSeverity();
}
});
List<Severity> sortedAscending = severityOrdering.sortedCopy(SEVERITY_LIST);
List<Severity> sortedDescending = severityOrdering.reverse().sortedCopy(SEVERITY_LIST);
}
enum Severity {
LOW(1), MEDIUM(2), HIGH(3);
private int severity;
Severity(int s) {
severity = s;
}
int getSeverity() {
return severity;
}
}
}
Working Solution:
Collections.sort(recommendations, new Comparator() {
private int priority(String s) {
if (s.equalsIgnoreCase("Low")) {
return 1;
} else if (s.equalsIgnoreCase("Medium")) {
return 2;
} else if (s.equalsIgnoreCase("High")) {
return 3;
} else {
return 0;
}
}
#Override
public int compare(Recommendation o1, Recommendation o2) {
return -1 * (priority(o1.getPriority()) - priority(o2.getPriority()));
}
});
If you want the DB to do this through JPA/Hibernate you could create a sort expression based on a simple case statement, assuming your entity is called Case:
Expression exp = criteriaBuilder.selectCase(root.get(Case_.priority)).when("High", 1).when("Medium", 2).otherwise(3);
queryBuilder.orderBy(orderDir.isAscending() ? criteriaBuilder.asc(exp) : criteriaBuilder.desc(exp));
Using case statements in an order by clause isn't great for performance, but solves it. Works with Oracle.
I have an enum that looks a little bit like this:
public enum Numbers {
ONE(1), TWO(2), THREE(3);
public final int num;
public Numbers(int num) {
this.num = num;
}
}
I want to be able to convert from argument to enum, for instance from the int 1 to the enum ONE. Is there any built-in mechanism in Java Enums to do this, or do I have to write my own logic for it?
Yes you have to write your own logic as the num variable is a part of your own logic :
public enum Numbers {
ONE(1), TWO(2), THREE(3);
public final int num;
private Numbers(int num) {
this.num = num;
}
public static Numbers getNumber(int i){
for(Numbers number : Numbers.values()){
if(i == number.num){
return number;
}
}
throw new IllegalArgumentException("This number doesn't exist");
}
}
If you want conversion from the ordinal you have to do it yourself. There is however automatic conversion from the name of an enum. Btw there is no need to specify the ordinal, that is done automatically and it starts with 0 and there is a ordinal() getter.
Enum.valueOf(Numbers.class, "ONE")
would return Numbers.ONE
If you can start your particular enum with ZERO instead, you could do
// ...
private static final Numbers[] list = values();
public static Numbers get(int which) { return list[i]; }
// ...
and ignore assigning indices to your enum.
EDIT: refactor safe option:
//..
private static final Map<Integer, Numbers> getter; static {
Numbers[] ns = values();
getter = new HashMap<Integer, Numbers>(ns.length, 1f);
for (Numbers n : ns) getter.put(n.num, n);
}
public static Numbers get(int which) { return getter.get(which); }
//..
this is also conducive to changing your index to whatever type you like and returns null instead of throwing an exception if you ask for garbage (which can be preferable).
Does Java allow something like good ol' C or even C# in the sense that you can define an enum with fields that grow in value automatically, and start at an optionally given value?
E.g.
In C or C#:
enum Foo { A = 10, B, C, D = 5000, E, Fish };
Yields A = 10, B = 11, C = 12, D = 5000, E = 5001, Fish = 5002.
In Java you can't specify the ordinal values explicitly at all. They always autoincrement, from 0, with no control over it.
If you want other custom values, you need to put them in constructor calls and store them yourself. You can get autoincrement, but it's icky as heck:
import java.util.EnumSet;
// Please don't ever use this code. It's here so you can point and laugh.
enum Foo
{
A(10), B, C, D(5000), E, Fish;
private static int nextValue;
private int value;
private Foo()
{
this(Counter.nextValue);
}
private Foo(int value)
{
this.value = value;
Counter.nextValue = value + 1;
}
public int getValue()
{
return value;
}
private static class Counter
{
private static int nextValue = 0;
}
}
public class Test
{
public static void main(String[] args)
{
for (Foo foo : EnumSet.allOf(Foo.class))
{
System.out.println(foo.name() + " " +
foo.ordinal() + " " +
foo.getValue());
}
}
}
Note the need for the nested class, because you can't access static fields within an enum constructor. Ick, ick, ick. Please don't do this.
This is a design choice of Java Enums to not support to change the ordinal values. Basically, they are not stable enough to depend on them. If you change the position of B and C in your example clients depending on the ordinal values are broken. This may happen unintentionally.
The problem is described in Effective Java Item 31: Use instance field instead of ordinals.
You can emulate the behavior in a stable manner:
enum X{
A(10), B(A), C(B), D(5000), E(D), F(E);
private final int value;
X(int value){
this.value = value;
}
X(X preceding){
this.value = preceding.getValue() + 1;
}
public int getValue() {
return value;
}
#Override
public String toString() {
return this.name() + "(" + this.value + ")";
}
static {
Set<Integer> values = new HashSet<Integer>();
for(X x : X.values()) values.add(x.value);
assert(values.size() == X.values().length); //no duplicates
}
}
With this definition you may change the order of the values without breaking clients.
Calling for(X x : X.values()) System.out.println(x); returns:
A(10)
B(11)
C(12)
D(5000)
E(5001)
F(5002)