Is there any way of converting an enum into a constant expression?
I want my switch operator to choose among the values of an enum, but I got a compile error "case expressions must be constant expressions", so I tried to declare it in a variable:
final int REG = MyEnum.REG.getIndex().intValue();
switch (service.getIndex()) {
case REG:
But I still get the same error. According to Oracle's documentation http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.28
A compile-time constant expression is an expression denoting a value
of primitive type or a String that does not complete abruptly and is
composed using only the following:
•Literals of primitive type and literals of type String
So it isn't working because I'm not using a literal. I think I will have to declare it as:
final int REG = 8;
But it'd be much better to link it to the enum. Is there any way of doing this?
EDIT
Turns out I don't need to use any final variable. It is just as simple as:
switch (service) {
case REG:
It didn't occur to me till I saw Andrea's comment. Thanks for your answers.
If possible, modify your getIndex() method so that it returns an enum instead of an integer. If this is not possible, you need to map the index to an enum element:
Given the following enum:
public enum Index {
ONE,
TWO,
THREE
}
you can map your index to an enum element by using
Index.values()[index]
Given your method Integer getIndex(), you can then do something like
switch(Index.values()[getIndex()])
case ONE : ...
break;
case TWO : ...
break;
case THREE : ...
break;
}
Note that this might throw an ArrayIndexOutOfBoundsException if you try to access an index within the enum which is larger than the number of enum elements (e.g. in the sample above, if getIndex() returns a value > 2).
I would encapsulate the expression Index.values()[getIndex()] into an enum method like valueOf(int index), similar to the default valueOf(String s). You can then also handle the valid array index check there (and for example return a special enum value if the index is out of range). Similarly, you can then also convert discrete values which have special meanings:
public enum Index {
ZERO,
ONE,
TWO,
THREE,
REG,
INVALID;
public static Index valueOf(int index) {
if (index == 8) {
return REG;
}
if (index >= values().length) {
return INVALID;
}
return values()[index];
}
}
This is an example only - in any case, it generally depends on the range of values you get from your getIndex() method, and how you want to map them to the enum elements.
You can then use it like
switch(Index.valueOf(service.getIndex())) {
case ZERO : ... break;
...
case REG : ... break;
...
}
See also Cast Int to enum in Java for some additional information (especially the hint that values() is an expensive operation since it needs to return a copy of the array each time it is called).
If you want specific numeral values assigned to enum constacts go like this
enum MyReg {
REG(8), OTHER(13);
public final int value;
MyReg(int value) {
this.value=value;
}
}
You then use it like this:
class Test {
public static void main(String[] args) {
MyReg reg = MyReg.REG;
switch (reg) {
case OTHER:
System.out.println(reg.value);
break;
case REG:
System.out.println(reg.value);
break;
}
}
}
Related
public static void doProcess(){
Integer [] intArray = {2,5,3,8,9};
returnArray(intArray);
//Expected output: 27 (sum of all number)
String[] strArray = {"Jack", "Daniel", "Richel"};
returnArray(strArray);
//Expected output: Jack Daniel Richel
Character[] charArray = {'A', 'X', 'E'};
returnArray(charArray);
//Expected output: AXE
}
I have the above code and the method below which need to complete.
private static <E> *** returnArray(E[] value) {
for(E ele : value) {
//code for expected output
}
//return Expected output
}
I need to fill above method as well as fill the return type which is here shown as ***.
I can't use separate method or introduce any new method.
Just adding to #Lino's answer to suggest a more generic approach: I will assume you want to sum up numbers (be it integers, floats or whatever) and concat anything else (strings, chars, even random objects like Duration or Instant). It's just that characters will be squashed together, while strings (or anything else, for that matter) will be joined with a space separator.
You can return Object or something a bit narrower that fits both numbers and strings (like Serializable or Comparable<?>); this is hardly useful, but keeps you from returning really random stuff.
Alternatively, you can decide to always return String and just return the sum as a string as well (for numbers).
The actual implementation can look pretty neat if you use Java8 streams:
// the `E` generic type brings you nothing, as you can't make use of it at compile time,
// so you can simply drop it and go with a mere `Object[]` array, as per #Lino
public static <E> Serializable returnArray(E[] values) {
// exclude null values if any (note that `E` proves itself useless already):
final Stream<?> stream = Stream.of(values).filter(Objects::nonNull);
if (values instanceof Number[]) {
// you can use mapToDouble and doubleValue, for a more accurate sum
return stream.map(Number.class::cast).mapToInt(Number::intValue).sum();
} else {
// squash characters, but use a space separator for anything else
final String separator = (values instanceof Character[]) ? "" : " ";
return stream.map(Object::toString).collect(Collectors.joining(separator));
}
}
You may want to use the instanceof, e.g. you can have a construct like this:
public static <E> Object returnArray(E[] value) {
if(value instanceof String[]) {
String[] strings = (String[]) value;
// concat the strings with spaces
} else if(value instanceof Integer[]) {
Integer[] ints = (Integer[]) value;
// sum the integers
} else if(value instanceof Character[]) {
Character[] chars = (Character[]) value;
// concat the characters
} else {
// throw exception or handle more cases
}
}
I've deliberatly left out the real code for the different operations as this should just point you into the right direction, (and I won't do your work for you).
The only real tricky thing is the return type. As it can't be E. It works with Strings and Integers, but will break with Characters as 'ABC' is not a valid char and thus you can't return it.
Note: using generics and instanceof breaks the whole concept of generics. You can aswell just remove it and have a method like this:
public static Object returnArray(Object[] value) {
Using Java 8, I have a Map that I need to convert to another Map replacing the key names and sometimes the value. E.g., when they turn out to be enums in which case I need to convert these enums to other constants (Strings and sometimes ints). I do not want to compare Strings, i.e. theEnum.name(), due to possible duplicates but would prefer to convert the Object to an enum and switch on it. However, I fail to find a way to convert the Object to a switchable enum. Enum.valueOf does not return something that can be switched upon (see the example below).
private void put(String key, Object value) {
if (value != null && value.getClass().isEnum()) {
Enum<?> val = (Enum<?>)value;
/* The below line has the following problem:
* Cannot switch on a value of type Enum.
* Only convertible int values, strings or enum variables are
* permitted */
switch (Enum.valueOf(val.getClass(), val.name())) {
case EmploymentType.FULL_TIME_EMPLOYEE : value = "Fast anställd";
break;
case ResidenceType.TENANCY : value = "Hyresrätt";
break;
default : break;
}
}
map.put(key, value);
}
I am aware that I could do:
private void put(String key, Object value) {
if (value != null && value.getClass().isEnum()) {
if (value.equals(EmploymentType.FULL_TIME_EMPLOYEE) value = "Fast anställd";
else if (value.equals(ResidenceType.TENANCY) value = "Hyresrätt";
}
map.put(key, value);
}
But is find that to be much less elegant and as easy to read as a switch statement would be. Is there a way to do that?
It looks like you have to deal with multiple enum types. But you can't switch accross multiple enum types in one switch statement. I think you could just use a Map<Enum<?>, Object> instead.
You could setup an HashMap like this:
Map<Enum<?>, Object> enumMap = new HashMap<>();
enumMap.put(EmploymentType.FULL_TIME_EMPLOYEE, "Fast anställd");
enumMap.put(ResidenceType.TENANCY, "Hyresrätt");
and use it like this:
if (enumMap.containsKey(value)) {
value = enumMap.get(value);
}
map.put(key, value);
One possibility would be to have the enums implement an interface with a getValue() or similar method. Even if it were possible to switch between different enums, that code isn't very elegant.
Then you could just write
if (value != null && value instanceof MyInterface) {
MyInterface foo = (MyInterface) value;
value = foo.getValue(); // Although it's a bit ugly to reassign a parameter
}
map.put(key, value);
Both Calculatos and Kaymans answers are good solutions. After trying them out i choose to go with Calculators as it is less intrusive and use the approach suggested to build another Map with only enums in the constructor and then use that enum map to convert the enums in the put method exactly as suggested i.e.
if (enumMap.containsKey(value)) {
value = enumMap.get(value);
}
map.put(key, value);
(note: edited question; the prior intent was not clear)
Consider this code:
public final class Foo
{
private enum X
{
VALUE1, VALUE2
}
public static void main(final String... args)
{
final X x = X.VALUE1;
switch (x) {
case VALUE1:
System.out.println(1);
break;
case VALUE2:
System.out.println(2);
}
}
}
This code works fine.
However, if I replace:
case VALUE1: // or VALUE2
with:
case X.VALUE1: // or X.VALUE2
then the compiler complains:
java: /path/to/Foo.java:whatever: an enum switch case label must be the unqualified name of an enumeration constant
SO suggests an answer with this quote from the JLS:
(One reason for requiring inlining of constants is that switch statements require constants on each case, and no two such constant values may be the same. The compiler checks for duplicate constant values in a switch statement at compile time; the class file format does not do symbolic linkage of case values.)
But this does not satisfy me. As far as I am concerned, VALUE1 and X.VALUE1 are exactly the same. The quoted text does not explain it at all for me.
Where in the JLS is it defined that enum values in switch statements have to be written this way?
SwitchLabel expects an EnumConstantName, which is defined as the enum constant identifier, which is unqualified:
EnumConstant:
Annotationsopt Identifier Argumentsopt ClassBodyopt
I may get result of any of the type , so i am defining enum this way
public enum Result
{
1, 2,3, 4,5, 6,7, 8
}
String resultvalue = calculateResult();
switch (Result .valueOf(resultvalue ))
{
}
But i am geting error at the Enum Declaration itself saying Mispalced Constructors .
Could anybody please help me
Those aren't valid identifiers for enum values, basically. You'll need to prefix them with a letter or _. You'll also need to make the identifiers unique - currently you've got 0010 four times...
Once you've sorted that out, the rest should probably be okay - but if you have any more problems, please post a short but complete program, rather than snippets.
0001 is not a valid Java identifier. A Java Identifier must not start with a digit.
Although I don't understand what you want to achieve and why you have duplicates. Something like that (maybe using an int instead of String) should work.
public enum Result {
One( "0001"),
Two( "0010")
...
private String val;
private Result(String val) {
this.val = val;
}
}
I an not sure why calcuate result would return a String. I would return a int here but...
String resultvalue = calculateResult();
switch (Integer.parseInt(resultvalue)) {
case 0b0001:
case 0b0010:
case 0b0110:
case 0b1010:
case 0b1100:
}
What is it that you are trying to achieve? If you need to:
parse an integer from a string, then
check that it's from a certain set of values, and finally
switch on its value,
then you don't need an enum. Just do it with Integer.parseInt(), Set.contains(), and switch.
The ordinal() method returns the ordinal of an enum instance.
How can I set the ordinal for an enum?
You can control the ordinal by changing the order of the enum, but you cannot set it explicitly like in C++. One workaround is to provide an extra method in your enum for the number you want:
enum Foo {
BAR(3),
BAZ(5);
private final int val;
private Foo(int v) { val = v; }
public int getVal() { return val; }
}
In this situation BAR.ordinal() == 0, but BAR.getVal() == 3.
You can't set it. It is always the ordinal of the constant definition. See the documentation for Enum.ordinal():
Returns the ordinal of this enumeration constant (its position in its enum declaration, where the initial constant is assigned an ordinal of zero). Most programmers will have no use for this method. It is designed for use by sophisticated enum-based data structures, such as EnumSet and EnumMap.
And actually - you should not need to. If you want some integer property, define one.
As the accepted answer points out, you can't set the ordinal. The closest you can get to this is with a custom property:
public enum MonthEnum {
JANUARY(1),
FEBRUARY(2),
MARCH(3),
APRIL(4),
MAY(5),
JUNE(6),
JULY(7),
AUGUST(8),
SEPTEMBER(9),
OCTOBER(10),
NOVEMBER(11),
DECEMBER(12);
MonthEnum(int monthOfYear) {
this.monthOfYear = monthOfYear;
}
private int monthOfYear;
public int asMonthOfYear() {
return monthOfYear;
}
}
Note: By default, enum values start at 0 (not 1) if you don't specify values. Also, the values do not have to increment by 1 for each item.
You can update ordinal using reflection:
private void setEnumOrdinal(Enum object, int ordinal) {
Field field;
try {
field = object.getClass().getSuperclass().getDeclaredField("ordinal");
field.setAccessible(true);
field.set(object, ordinal);
} catch (Exception ex) {
throw new RuntimeException("Can't update enum ordinal: " + ex);
}
}
From http://download.oracle.com/javase/1.5.0/docs/api/java/lang/Enum.html
public final int ordinal()Returns the ordinal of this enumeration constant (its position in its enum declaration, where the initial constant is assigned an ordinal of zero). Most programmers will have no use for this method. It is designed for use by sophisticated enum-based data structures, such as EnumSet and EnumMap.
Returns:
the ordinal of this enumeration constant
If you have
public enum Day {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
THURSDAY, FRIDAY, SATURDAY
}
then SUNDAY has an ordinal of 0, MONDAY is 1, and so on...
Check out the Java Enum examples and docs
Returns the ordinal of this enumeration constant (its position in its enum declaration, where the initial constant is assigned an ordinal of zero). Most programmers will have no use for this method. It is designed for use by sophisticated enum-based data structures, such as EnumSet and EnumMap.
The easy answer: just change the order of the constants. The first defined will be 0, the second will be 1, etc. However, this may not be practical if you have constantly changing code, or enums will many many values. You can define a custom method to work around the default ordinal, but MAKE SURE it is well documented to avoid confusion!
public enum Values
{
ONE, TWO, THREE, FOUR;
public int getCustomOrdinal()
{
if(this == ONE)
{
return 3;
}
else if(this == TWO)
{
return 0;
}
...
}
}