When I'm reading Effective Java, the author told me that a single-element enum
type is the best way to implement a singleton, because we don't have to consider sophisticated serialization or reflection attacks. This means we cannot create an instance of enum
using reflection, right?
I have done some tests, with an enum
class here:
public enum Weekday {}
Then I tried to create an instance of Weekday
:
Class<Weekday> weekdayClass = Weekday.class;
Constructor<Weekday> cw = weekdayClass.getConstructor(null);
cw.setAccessible(true);
cw.newInstance(null);
As you know, it doesn't work. When I change the key word enum
to class
, it works. I want to know why. Thank you.
It is possible to create new enum instance in runtime - but it is very bad idea and might break in any update. You can use unsafe or reflections for this.
Like at this example enum:
We can use
On java 9 this might not compile due to usage of internal class as I described that in comment - you can skip that using unsafe or even more reflections.
But then we also need to add that constant to enum itself, so Enum.values() will return valid list, we can do this by changing value of final field using good old trick to make final field non-final again:
And then just change that field to new value that include our new field:
There is also another field that store enum constant, so it is important to do similar trick to it too:
private volatile transient T[] enumConstants = null;
- inClass.class
, note that it can be null - java will regenerate them on next usage.private volatile transient Map<String, T> enumConstantDirectory = null;
- inClass.class
, note that it can be null too, same as field above.So just set them to null using reflections and your new value is ready to use.
The only impossible thing without editing class using instrumentation or other tricks is to add real field to that enum for our new value.
Also it is possible to create new enum instance using Unsafe class:
But unsafe class does not call the constructor, so you need to init all fields manually...
Note that you also need to initialize internal enum fields.
Also using unsafe it should be possible to declare new class to create new instance of abstract enum classes. I used javassist library to reduce code needed to generate new class:
This will print 5 and then 3. Note that this is impossible to enum classes that does not contain subclasses - so without any overriden methods, as then enum is declared as final class.
Source: https://blog.gotofinal.com/java/diorite/breakingjava/2017/06/24/dynamic-enum.html
Enums has been designed to be treated as constant objects. It overrides readObject and throws invalid object exception to prevent default serialization. Also it overrides clone() and throws clone not supported exception. As far as reflection is concerned, the constructor of Enum is protected.So if you use above code it will throw NoSuchMethodFound.
Even if you use getDeclaredConstructor() instead of getConstructor, you should get the same exception. I assume its been restricted through SecurityManager in java.
It is correct that new instances of an enum class cannot be created retro-actively, not even with reflection.
The following code demonstrates this:
Usually, this should work. But in the case of enums, this is special-cased in
Constructor#newInstance
:Thus, we receive the following exception when trying to instantiate a new enum instance:
I assume that the last approach (which will probably be successful, because no checks or constructors are run) involves
sun.misc.Unsafe#allocateInstance
.This is built into the language. From the Java Language Specification (§8.9):
The whole purpose of this is to allow the safe use of
==
to compareEnum
instances.EDIT: See the answer by @GotoFinal for how to break this "guarantee" using reflection.
So if your objective is to persistent and then reconstructed the enum information. You will need to persist the enumClassName and its value.
This may be reviving a dead post, but you can get an instance of every constant declared using
Weekday.class.getEnumConstants()
. This returns an array of all the constatants, where getting a single instance is trivial,getEnumConstants()[0]
.