I am very familiar with C# but starting to work more in Java. I expected to learn that enums in Java were basically equivalent to those in C# but apparently this is not the case. Initially I was excited to learn that Java enums could contain multiple pieces of data which seems very advantageous (http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html). However, since then I have found a lot of features missing that are trivial in C#, such as the ability to easily assign an enum element a certain value, and consequently the ability to convert an integer to an enum without a decent amount of effort (i.e. Convert integer value to matching Java Enum).
So my question is this: is there any benefit to Java enums over a class with a bunch of public static final fields? Or does it just provide more compact syntax?
EDIT: Let me be more clear. What is the benefit of Java enums over a class with a bunch of public static final fields of the same type? For example, in the planets example at the first link, what is the advantage of an enum over a class with these public constants:
public static final Planet MERCURY = new Planet(3.303e+23, 2.4397e6);
public static final Planet VENUS = new Planet(4.869e+24, 6.0518e6);
public static final Planet EARTH = new Planet(5.976e+24, 6.37814e6);
public static final Planet MARS = new Planet(6.421e+23, 3.3972e6);
public static final Planet JUPITER = new Planet(1.9e+27, 7.1492e7);
public static final Planet SATURN = new Planet(5.688e+26, 6.0268e7);
public static final Planet URANUS = new Planet(8.686e+25, 2.5559e7);
public static final Planet NEPTUNE = new Planet(1.024e+26, 2.4746e7);
As far as I can tell, casablanca's answer is the only one that satisfies this.
public static final
fields, which may be typed values and not necessarily int
s.
S
, so now we're talking about probably passing them into functions, etc. Do we need type-saftey? Well, no, but then most people consider c-style types of "everything's a void*
" good style and it can stop bugs (especially if passing more than one enum/string parameter!). Also it puts the constants into their own namespace, etc. Contrary to that, there's no real advantage to just have plain variables.
int
s, there is no type safety because one could pass any value. Typed objects, on the other hand, are no different from enums in terms of type safety.
Type safety and value safety. Guaranteed singleton. Ability to define and override methods. Ability to use values in switch statement case statements without qualification. Built-in sequentialization of values via ordinal(). Serialization by name not by value, which offers a degree of future-proofing. EnumSet and EnumMap classes.
Technically one could indeed view enums as a class with a bunch of typed constants, and this is in fact how enum constants are implemented internally. Using an enum
however gives you useful methods (Enum javadoc) that you would otherwise have to implement yourself, such as Enum.valueOf
.
.values()
to iterate over the list of values.
Nobody mentioned the ability to use them in switch
statements; I'll throw that in as well.
This allows arbitrarily complex enums to be used in a clean way without using instanceof
, potentially confusing if
sequences, or non-string/int switching values. The canonical example is a state machine.
Planet
instances when Planet
is not an enum
?
The primary advantage is type safety. With a set of constants, any value of the same intrinsic type could be used, introducing errors. With an enum only the applicable values can be used.
For example
public static final int SIZE_SMALL = 1;
public static final int SIZE_MEDIUM = 2;
public static final int SIZE_LARGE = 3;
public void setSize(int newSize) { ... }
obj.setSize(15); // Compiles but likely to fail later
vs
public enum Size { SMALL, MEDIUM, LARGE };
public void setSize(Size s) { ... }
obj.setSize( ? ); // Can't even express the above example with an enum
setSize(null)
in your second example, but it is likely to fail much sooner than the first example's error.
There is less confusion. Take Font
for instance. It has a constructor that takes the name of the Font
you want, its size and its style (new Font(String, int, int)
). To this day I cannot remember if style or size goes first. If Font
had used an enum
for all of its different styles (PLAIN
, BOLD
, ITALIC
, BOLD_ITALIC
), its constructor would look like Font(String, Style, int)
, preventing any confusion. Unfortunately, enum
s weren't around when the Font
class was created, and since Java has to maintain reverse compatibility, we will always be plagued by this ambiguity.
Of course, this is just an argument for using an enum
instead of public static final
constants. Enums are also perfect for singletons and implementing default behavior while allowing for later customization (I.E. the strategy pattern). An example of the latter is java.nio.file
's OpenOption
and StandardOpenOption
: if a developer wanted to create his own non-standard OpenOption
, he could.
enum
without using varargs or a Set
to take an arbitrary number of them.
interface Foo { public void bar(String baz); }
. Someone makes a class which invokes bar
: someFoo.bar(baz = "hello");
. I change the signature of Foo::bar
to public void bar(String foobar)
. Now the person who called someFoo
will need to modify their code if they still want it to work.
There are many good answers here, but none mentiones that there are highly optimized implementations of the Collection API classes/interfaces specifically for enums:
EnumSet
EnumMap
These enum specific classes only accept Enum
instances (the EnumMap
only accept Enum
s only as keys), and whenever possible, they revert to compact representation and bit manipulation in their implementation.
What does this mean?
If our Enum
type has no more that 64 elements (most of real-life Enum
examples will qualify for this), the implementations store the elements in a single long
value, each Enum
instance in question will be associated with a bit of this 64-bit long long
. Adding an element to an EnumSet
is simply just setting the proper bit to 1, removing it is just setting that bit to 0. Testing if an element is in the Set
is just one bitmask test! Now you gotta love Enum
s for this!
What does this mean?
section. I knew there was a divide in implementation at size 64, but I didn't actually know why
EnumSet
has the same optimizations for types containing more than 64 constants as well. It just has to use a long[]
array instead of a single long
. And while reducing add
, remove
, and contains
operations to single bit operations is great, the real fun starts with operations like addAll
, removeAll
, and retainAll
which do not go bit by bit but process 64 elements (in other words, all of them, usually) at once.
example:
public class CurrencyDenom {
public static final int PENNY = 1;
public static final int NICKLE = 5;
public static final int DIME = 10;
public static final int QUARTER = 25;}
Limitation of java Constants
1) No Type-Safety: First of all it’s not type-safe; you can assign any valid int value to int e.g. 99 though there is no coin to represent that value.
2) No Meaningful Printing: printing value of any of these constant will print its numeric value instead of meaningful name of coin e.g. when you print NICKLE it will print "5" instead of "NICKLE"
3) No namespace: to access the currencyDenom constant we need to prefix class name e.g. CurrencyDenom.PENNY instead of just using PENNY though this can also be achieved by using static import in JDK 1.5
Advantage of enum
1) Enums in Java are type-safe and has there own name-space. It means your enum will have a type for example "Currency" in below example and you can not assign any value other than specified in Enum Constants.
public enum Currency {PENNY, NICKLE, DIME, QUARTER};
Currency coin = Currency.PENNY; coin = 1; //compilation error
2) Enum in Java are reference type like class or interface and you can define constructor, methods and variables inside java Enum which makes it more powerful than Enum in C and C++ as shown in next example of Java Enum type.
3) You can specify values of enum constants at the creation time as shown in below example: public enum Currency {PENNY(1), NICKLE(5), DIME(10), QUARTER(25)}; But for this to work you need to define a member variable and a constructor because PENNY (1) is actually calling a constructor which accepts int value , see below example.
public enum Currency {
PENNY(1), NICKLE(5), DIME(10), QUARTER(25);
private int value;
private Currency(int value) {
this.value = value;
}
};
Reference: https://javarevisited.blogspot.com/2011/08/enum-in-java-example-tutorial.html
The first benefit of enums, as you have already noticed, is syntax simplicity. But the main point of enums is to provide a well-known set of constants which, by default, form a range and help to perform more comprehensive code analysis through type & value safety checks.
Those attributes of enums help both a programmer and a compiler. For example, let's say you see a function that accepts an integer. What that integer could mean? What kind of values can you pass in? You don't really know right away. But if you see a function that accepts enum, you know very well all possible values you can pass in.
For the compiler, enums help to determine a range of values and unless you assign special values to enum members, they are well ranges from 0 and up. This helps to automatically track down errors in the code through type safety checks and more. For example, compiler may warn you that you don't handle all possible enum values in your switch statement (i.e. when you don't have default
case and handle only one out of N enum values). It also warns you when you convert an arbitrary integer into enum because enum's range of values is less than integer's and that in turn may trigger errors in the function that doesn't really accept an integer. Also, generating a jump table for the switch becomes easier when values are from 0 and up.
This is not only true for Java, but for other languages with a strict type-checking as well. C, C++, D, C# are good examples.
An enum is implictly final, with a private constructors, all its values are of the same type or a sub-type, you can obtain all its values using values()
, gets its name()
or ordinal()
value or you can look up an enum by number or name.
You can also define subclasses (even though notionally final, something you can't do any other way)
enum Runner implements Runnable {
HI {
public void run() {
System.out.println("Hello");
}
}, BYE {
public void run() {
System.out.println("Sayonara");
}
public String toString() {
return "good-bye";
}
}
}
class MYRunner extends Runner // won't compile.
enum Benefits:
Enums are type-safe, static fields are not There is a finite number of values (it is not possible to pass non-existing enum value. If you have static class fields, you can make that mistake) Each enum can have multiple properties (fields/getters) assigned - encapsulation. Also some simple methods: YEAR.toSeconds() or similar. Compare: Colors.RED.getHex() with Colors.toHex(Colors.RED)
"such as the ability to easily assign an enum element a certain value"
enum EnumX{
VAL_1(1),
VAL_200(200);
public final int certainValue;
private X(int certainValue){this.certainValue = certainValue;}
}
"and consequently the ability to convert an integer to an enum without a decent amount of effort" Add a method converting int to enum which does that. Just add static HashMap
If you really want to convert ord=VAL_200.ordinal() back to val_200 just use: EnumX.values()[ord]
The biggest advantage is enum Singletons are easy to write and thread-safe :
public enum EasySingleton{
INSTANCE;
}
and
/**
* Singleton pattern example with Double checked Locking
*/
public class DoubleCheckedLockingSingleton{
private volatile DoubleCheckedLockingSingleton INSTANCE;
private DoubleCheckedLockingSingleton(){}
public DoubleCheckedLockingSingleton getInstance(){
if(INSTANCE == null){
synchronized(DoubleCheckedLockingSingleton.class){
//double checking Singleton instance
if(INSTANCE == null){
INSTANCE = new DoubleCheckedLockingSingleton();
}
}
}
return INSTANCE;
}
}
both are similar and it handled Serialization by themselves by implementing
//readResolve to prevent another instance of Singleton
private Object readResolve(){
return INSTANCE;
}
Another important difference is that java compiler treats static final
fields of primitive types and String as literals. It means these constants become inline. It's similar to C/C++
#define
preprocessor. See this SO question. This is not the case with enums.
Enums can be local
As of Java 16, an enum can be defined locally (within a method). This scope is in addition to being able to define an enum as nested or as separate class.
This new local definition scope came along with the new records feature. See JEP 395: Records for details. Enums, interfaces, and records can all be defined locally in Java 16+.
In contrast, public static final
fields always have global scope.
static final
fields. Their scope would be the same as for the constants of a local enum
. However, when you’re within an instance method, ordinary local classes still have an implicit reference to the surrounding this
, which prevents them from being used like an enum
.
I think an enum
can't be final
, because under the hood compiler generates subclasses for each enum
entry.
More information From source
final
. Otherwise, the enum
type itself will be final
.
There are many advantages of enums that are posted here, and I am creating such enums right now as asked in the question. But I have an enum with 5-6 fields.
enum Planet{
EARTH(1000000, 312312321,31232131, "some text", "", 12),
....
other planets
....
In these kinds of cases, when you have multiple fields in enums, it is much difficult to understand which value belongs to which field as you need to see constructor and eye-ball.
Class with static final
constants and using Builder
pattern to create such objects makes it more readable. But, you would lose all other advantages of using an enum, if you need them. One disadvantage of such classes is, you need to add the Planet
objects manually to the list/set
of Planets.
I still prefer enum over such class, as values()
comes in handy and you never know if you need them to use in switch
or EnumSet
or EnumMap
in future :)
Main reason: Enums help you to write well-structured code where the semantic meaning of parameters is clear and strongly-typed at compile time - for all the reasons other answers have given.
Quid pro quo: in Java out of the box, an Enum's array of members is final. That's normally good as it helps value safety and testing, but in some situations it could be a drawback, for example if you are extending existing base code perhaps from a library. In contrast, if the same data is in a class with static fields you can easily add new instances of that class at runtime (you might also need to write code to add these to any Iterable you have for that class). But this behaviour of Enums can be changed: using reflection you can add new members at runtime or replace existing members, though this should probably only be done in specialised situations where there is no alternative: i.e. it's a hacky solution and may produce unexpected issues, see my answer on Can I add and remove elements of enumeration at runtime in Java.
You can do :
public enum Size { SMALL(1), MEDIUM(2), LARGE(3) };
private int sizeValue;
Size(sizeValue) {this.sizeValue = value; }
So with this you can get size value like this SMALL.getSizeValue();
If you want to set sizes Enums are not for you, if you will be only define constants and fixed values are fine.
Check this link maybe can help you
Success story sharing
switch
statements that were the original motivation for using anEnum
at all.