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)
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 am trying to create a enum with values 1,-1,0 but while mapping the data unknown Ordinal value for -1 is coming. Below is the piece of code for the enum . How the enum can be use to retrieve the ordinal value for -1
#RequiredArgsConstructor
public enum Number {
MULTIPLE(0, -1, "MULTIPLE"),
ZERO(1, 0, "ZERO"),
ONE(2, 1, "ONE");
private static final Map<Integer, Number> DOCUMENT_CARDINALITY_ID = Arrays.stream(values())
.collect(Collectors.toMap(Number :: intValue, Function.identity()));
private static final Map<String, Number> DOCUMENT_CARDINALITY_BY_CODE = Arrays.stream(values())
.collect(Collectors.toMap(Number :: toString, Function.identity()));
private final int value;
private final int id;
private final String code;
public static Number of(int id) {
isTrue(DOCUMENT_CARDINALITY_ID.containsKey(id), "the id " + id + " is not valid!");
return DOCUMENT_CARDINALITY_ID.get(id);
}
public static Number of(String code) {
notNull(code, "the code is null!");
isTrue(DOCUMENT_CARDINALITY_BY_CODE.containsKey(code), "the code " + code + " is not valid!");
return DOCUMENT_CARDINALITY_BY_CODE.get(code);
}
public int intValue() {
if (id == -1) {
return 0;
} else if (id == 0) {
return 1;
} else {
return 2;
}
}
#Override
public String toString() {
return code;
}
}
You cannot set the ordinal value of an Enum. The ordinal value is set based upon its position during Enum declaration. In this case, the ordinals might look like
0 MULTIPLE
1 ZERO
2 ONE
See this previous post for more details. Also your spring error is occurring outside of this class and so, I have no idea what would be triggering it
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();
}
I am reading Thinking in java book, which is quite interesting, there is an example in the book, and the behavior of the example and the output is not as I expected.
The Example:
package net.mindview.util;
import static net.mindview.util.Print.*;
class Shared {
private int refcount = 0;
private static long counter = 0;
private final long id = counter++;
public Shared() {
print("Creating " + this);
}
public void addRef() {
refcount++;
}
protected void dispose() {
if (--refcount == 0)
print("Disposing " + this);
}
public String toString() {
System.out.println(id);
return "Shared " + id;
}
}
class Composing {
private Shared shared;
private static long counter = 0;
private final long id = counter++;
public Composing(Shared shared) {
print("Creating " + this);
this.shared = shared;
this.shared.addRef();
}
protected void dispose() {
print("disposing " + this);
shared.dispose();
}
public String toString() {
return "Composing " + id;
}
}
public class ReferenceCounting {
public static void main(String[] args) {
Shared shared = new Shared();
Composing[] composing = { new Composing(shared), new Composing(shared),
new Composing(shared), new Composing(shared),
new Composing(shared) };
for (Composing c : composing)
c.dispose();
}
}
This is an example of dispose method, which I fully understand, my question is in the value of id in Shared class.
As I am learning from Thinking in java, the initialization of the field and objects should occur before any method get called, even the Constructor, But the output for the constructor of Shared class is "Creating Shared 0" which the value of id is considered to be 0 in spite of private final long id = counter++; has took a place and now as I imagine the id value should be 1 not zero. Could any one explain to me the behavior.
counter++ is post-increment, this means the value is assigned before incrementation. pre-increment on the other side would show the behavior you expected.
This is an example of post-increment. In other words, the old value is stored in a temporary variable and returned after incrementing. It is the equivalent of:
int i = 0;
int tmp = i;
i += 1;
System.out.println(tmp); //prints 0
The opposite is pre-increment, where the incremented value is returned:
int i = 0;
System.out.println(++i); //prints 1
When you say counter++ it is a post-increment operator (on the next line, counter is increased by one). Based on your question, I think you expected the behavior of the pre-increment operator. Change
final long id = counter++;
to something like
final long id = ++counter;
or even
final long id = counter + 1;
counter++;
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.