18

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.

Radiodef
  • 37,180
  • 14
  • 90
  • 125
xing.zhang
  • 359
  • 1
  • 2
  • 13
  • You could create an enum class on-the-fly using bytecode generation. But in that case, the enum wouldn't exist at compile-time, so you wouldn't be able to refer to it in the rest of your code (you'd only be able to access it via reflection, which is a run-time and not a compile-time mechanism). – Andrew Spencer Mar 08 '12 at 08:42
  • It is possible to create enum instance at runtime, just not guaranteed to be possible so doing that is always bad idea - but if you need a hack like that for some weird reason - you can do this without any issues on all current java versions under hotspot. (you can see my answer here for more) – GotoFinal Jul 10 '18 at 09:40

6 Answers6

20

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:

public enum Monster {
    ZOMBIE(Zombie.class, "zombie"),
    ORK(Ork.class, "ork"),
    WOLF(Wolf.class, "wolf");
    private final Class<? extends Entity> entityClass;
    private final String                  entityId;
    Monster(Class<? extends Entity> entityClass, String entityId) {
        this.entityClass = entityClass;
        this.entityId = "monster:" + entityId;
    }
    public Class<? extends Entity> getEntityClass() { return this.entityClass; }
    public String getEntityId() { return this.entityId; }
    public Entity create() {
        try { return entityClass.newInstance(); }
        catch (InstantiationException | IllegalAccessException e) { throw new InternalError(e); }
    }
}

We can use

Class<Monster> monsterClass = Monster.class;
// first we need to find our constructor, and make it accessible
Constructor<?> constructor = monsterClass.getDeclaredConstructors()[0];
constructor.setAccessible(true);

// this is this same code as in constructor.newInstance, but we just skipped all that useless enum checks ;)
Field constructorAccessorField = Constructor.class.getDeclaredField("constructorAccessor");
constructorAccessorField.setAccessible(true);
// sun.reflect.ConstructorAccessor -> internal class, we should not use it, if you need use it, it would be better to actually not import it, but use it only via reflections. (as package may change, and will in java 9+)
ConstructorAccessor ca = (ConstructorAccessor) constructorAccessorField.get(constructor);
if (ca == null) {
    Method acquireConstructorAccessorMethod = Constructor.class.getDeclaredMethod("acquireConstructorAccessor");
    acquireConstructorAccessorMethod.setAccessible(true);
    ca = (ConstructorAccessor) acquireConstructorAccessorMethod.invoke(constructor);
}
// note that real constructor contains 2 additional parameters, name and ordinal
Monster enumValue = (Monster) ca.newInstance(new Object[]{"CAERBANNOG_RABBIT", 4, CaerbannogRabbit.class, "caerbannograbbit"});// you can call that using reflections too, reflecting reflections are best part of java ;)

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:

static void makeAccessible(Field field) throws Exception {
    field.setAccessible(true);
    Field modifiersField = Field.class.getDeclaredField("modifiers");
    modifiersField.setAccessible(true);
    modifiersField.setInt(field, field.getModifiers() & ~ Modifier.FINAL);
}

And then just change that field to new value that include our new field:

Field valuesField = Monster.class.getDeclaredField("$VALUES");
makeAccessible(valuesField);
// just copy old values to new array and add our new field.
Monster[] oldValues = (Monster[]) valuesField.get(null);
Monster[] newValues = new Monster[oldValues.length + 1];
System.arraycopy(oldValues, 0, newValues, 0, oldValues.length);
newValues[oldValues.length] = enumValue;
valuesField.set(null, newValues);

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; - in Class.class, note that it can be null - java will regenerate them on next usage.
private volatile transient Map<String, T> enumConstantDirectory = null; - in Class.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:

public static void unsafeWay() throws Throwable {
    Constructor<?> constructor = Unsafe.class.getDeclaredConstructors()[0];
    constructor.setAccessible(true);
    Unsafe unsafe = (Unsafe) constructor.newInstance();
    Monster enumValue = (Monster) unsafe.allocateInstance(Monster.class);
}

But unsafe class does not call the constructor, so you need to init all fields manually...

Field ordinalField = Enum.class.getDeclaredField("ordinal");
makeAccessible(ordinalField);
ordinalField.setInt(enumValue, 5);

Field nameField = Enum.class.getDeclaredField("name");
makeAccessible(nameField);
nameField.set(enumValue, "LION");

Field entityClassField = Monster.class.getDeclaredField("entityClass");
makeAccessible(entityClassField);
entityClassField.set(enumValue, Lion.class);

Field entityIdField = Monster.class.getDeclaredField("entityId");
makeAccessible(entityIdField);
entityIdField.set(enumValue, "Lion");

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:

public class Test {
    public static void main(String[] args) throws Exception {
        System.out.println(MyEnum.VALUE.getSomething());

        ClassPool classPool = ClassPool.getDefault();
        CtClass enumCtClass = classPool.getCtClass(MyEnum.class.getName());
        CtClass ctClass = classPool.makeClass("com.example.demo.MyEnum$2", enumCtClass);

        CtMethod getSomethingCtMethod = new CtMethod(CtClass.intType, "getSomething", new CtClass[0], ctClass);
        getSomethingCtMethod.setBody("{return 3;}");
        ctClass.addMethod(getSomethingCtMethod);

        Constructor<?> unsafeConstructor = Unsafe.class.getDeclaredConstructors()[0];
        unsafeConstructor.setAccessible(true);
        Unsafe unsafe = (Unsafe) unsafeConstructor.newInstance();

        MyEnum newInstance = (MyEnum) unsafe.allocateInstance(ctClass.toClass());
        Field singletonInstance = MyEnum.class.getDeclaredField("VALUE");
        makeAccessible(singletonInstance);
        singletonInstance.set(null, newInstance);

        System.out.println(MyEnum.VALUE.getSomething());
    }

    static void makeAccessible(Field field) throws Exception {
        field.setAccessible(true);
        Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        modifiersField.setInt(field, field.getModifiers() & ~ Modifier.FINAL);
    }
}

enum MyEnum {
    VALUE {
        @Override
        public int getSomething() {
            return 5;
        }
    };

    public abstract int getSomething();
}

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

GotoFinal
  • 3,585
  • 2
  • 18
  • 33
  • That's pretty mind-blowing. I haven't played around with this yet, but my first impression is that you've essentially extended the `Monster` enum with a new value at run time and then created an instance of that value. Can this method be used to create a second instance of an existing enum value? If so, I wonder what the security implications of that might be. – Ted Hopp Jul 10 '18 at 14:10
  • @TedHopp you mean modifying some enum constant? it is just a field, so it is pretty easy, you can see example in my answer here: https://stackoverflow.com/a/51246456/4378853 security implications? If you have access to code you can do whatever you want, there is no such thing as security then. Unless you are thinking about executing external java code under security manager - then you always need to block reflections or security manager is useless. ( – GotoFinal Jul 10 '18 at 17:21
  • I mean create a second instance of a particular enum value. The challenge is to have two variables, `a` and `b`, of type `MONSTER`, such that `a.name().equals(b.name())` evaluates to `true` but `a == b` evaluates to `false`. – Ted Hopp Jul 10 '18 at 20:10
  • @TedHopp no issue with that, you can create duplicated names/ordinals - you can even override what Enum.valueOf should return by modifying cached values. So you can make `MyEnum.INST != MyEnum.valueOf(MyEnum.INST.name())` – GotoFinal Jul 10 '18 at 20:57
  • My guess is that eventually the Java folks are going to try to block this approach to messing with the enum semantics. Meanwhile, congrats on working this all out. – Ted Hopp Jul 10 '18 at 23:14
  • @TedHopp blocking stuff like that is just pointless, sometimes you just need to "hack" something, as you have some broken library and you can't change it. And to do that you must use a lot of reflections or even internal code - so everyone know that this isn't good solution. But oracle is weird and already are trying to block reflections between modules for no real reason... – GotoFinal Jul 11 '18 at 06:41
  • This was very helpful in figuring out how to get this to work. Although I got a warning using the constructor, I was able to work around it (but still use the values changes) using Mockito. – Phil Sep 14 '18 at 16:21
  • Im seriously considering implementing this into a production ready application unit testing configured in the deployment pipeline just to prove how absurd it is to point at us because we're missing the coverage of those unreachable lines with the mantra: "if it's unbreachable shouldn't be there" and just watch their faces when this explodes in some update. I won't, of course, but I bet my developers would love it xD – DGoiko Aug 28 '22 at 18:25
20

This is built into the language. From the Java Language Specification (§8.9):

It is a compile-time error to attempt to explicitly instantiate an enum type (§15.9.1). The final clone method in Enum ensures that enum constants can never be cloned, and the special treatment by the serialization mechanism ensures that duplicate instances are never created as a result of deserialization. Reflective instantiation of enum types is prohibited. Together, these four things ensure that no instances of an enum type exist beyond those defined by the enum constants.

The whole purpose of this is to allow the safe use of == to compare Enum instances.

EDIT: See the answer by @GotoFinal for how to break this "guarantee" using reflection.

Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
  • 1
    The `enum` is actually compiled into a `final class` that extends `java.lang.Enum` with a `private` constructor, and that does not actually prevent reflection instantiation if `setAccessible(true);` is called. I'm still wondering how is that prevented? – Ahmad Y. Saleh Mar 08 '12 at 08:10
  • 1
    @AhmadY.Saleh - See [the answer by soc](http://stackoverflow.com/a/14432647/535871). – Ted Hopp Jul 27 '14 at 01:38
  • 4
    While you cannot instantiate an enum by reflection, you can obtain an enum through reflection, and that is possibly what was intended. Example `Enum.valueOf((Class) Class.forName("javax.swing.GroupLayout$Alignment"), "CENTER")` – Enwired Jun 01 '17 at 21:02
  • @Enwired - Yes, you can _obtain_ a reference to an existing instance of an `enum` with reflection, but it seems to me that OP was wondering why new instances can't be created with that (or any other) mechanism. – Ted Hopp Jun 02 '17 at 01:33
  • @TedHopp Yes, I understand. But I came here looking for a way to obtain an enum via reflection, so added the comment to help others. (My use case is that a certain jar file is optional. When it is present, I want to make use of features in it that involve an enum.) – Enwired Jun 02 '17 at 20:34
  • It is possible to create enum instance at runtime, just not guaranteed to be possible so doing that is always bad idea - but if you need a hack like that for some weird reason - you can do this without any issues on all current java versions under hotspot. (you can see my answer here for more) – GotoFinal Jul 10 '18 at 09:40
11

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].

Paul S
  • 111
  • 1
  • 4
6

So if your objective is to persistent and then reconstructed the enum information. You will need to persist the enumClassName and its value.

public enum DaysOfWeek{ Mon, Tue, Wed, Thu, Fri, Sat, Sun }

DaysOfWeek dow = DaysOfWeek.Tue;
String value = dow.toString();
String enumClassName = dow.getClass().getName();

// Persist value and enumClassName
// ...

// Reconstitute the data 
Class clz = Class.forName(enumClassName);
Object o = Enum.valueOf(clz, value);
DaysOfWeek dow2 = (DaysOfWeek)o;
System.out.println(dow2);
Kyle
  • 71
  • 1
  • 1
2

It is correct that new instances of an enum class cannot be created retro-actively, not even with reflection.

The following code demonstrates this:

val weekdayClass = classOf[Weekday]
val weekdayConstructor = weekdayClass getDeclaredConstructor (classOf[String], classOf[Int])
weekdayConstructor setAccessible true
weekdayConstructor newInstance ("", Integer.valueOf(0))

Usually, this should work. But in the case of enums, this is special-cased in Constructor#newInstance:

if ((clazz.getModifiers() & Modifier.ENUM) != 0)
    throw new IllegalArgumentException("Cannot reflectively create enum objects");

Thus, we receive the following exception when trying to instantiate a new enum instance:

java.lang.IllegalArgumentException: Cannot reflectively create enum objects
        at java.lang.reflect.Constructor.newInstance(Constructor.java:520)
        ...

I assume that the last approach (which will probably be successful, because no checks or constructors are run) involves sun.misc.Unsafe#allocateInstance.

soc
  • 27,983
  • 20
  • 111
  • 215
0

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.