1

UPDATE: My question is not related with Instantiate enum class. That question just needs to instantiate the enum with one of the existing values. I am asking: why the Reflection API throws NoSuchMethodException for a method that really exists?.

The following code runs without error, or not, depending on whether Xpto is declared as class or enum.

class Xpto {
  // Bar; // include this for enum declaration
  private Xpto() {      
  }
}

public class App {
  public static void main(String[] args) throws Exception{
    Constructor<Xpto> constructor = Xpto.class.getDeclaredConstructor();
    constructor.setAccessible(true);
    constructor.newInstance();
  }
}

In both cases javap shows a constructor private Xpto(). If Xpto is a class then the result of javap -private is:

class Xpto {
  private Xpto();
}

If Xpto is a enum then the result of javap -private is:

final class Xpto extends java.lang.Enum<Xpto> {
  ...
  private Xpto();
  static {};
}

Yet for latter it throws an exception:

Exception in thread "main" java.lang.NoSuchMethodException: Xpto.<init>()
    at java.lang.Class.getConstructor0(Unknown Source)

In both cases the result of the compilation is a class with a private constructor. The use of the reflection API in Xpto.class.getDeclaredConstructor(); does not report an error regarding the fact of Xpto being a enum, ot not. It just throws that there is no such method Xpto.<init>() for the case of a enum. This is not true. Because that constructor exists.

Miguel Gamboa
  • 8,855
  • 7
  • 47
  • 94
  • then why use an enum? – Stultuske Oct 25 '18 at 12:55
  • I am not the provider of that enum. But I need it with a different internal value. – Miguel Gamboa Oct 25 '18 at 12:55
  • 1
    Possible duplicate of [Instantiate enum class](https://stackoverflow.com/questions/16851377/instantiate-enum-class) – Stultuske Oct 25 '18 at 13:00
  • Well, the goal of an enum is to make it impossible to create new values – Stultuske Oct 25 '18 at 13:00
  • I have already updated my question to explain why it is not the same of [Instantiate enum class](https://stackoverflow.com/q/16851377/1140754) – Miguel Gamboa Oct 25 '18 at 13:10
  • 3
    "an enum type definition that I need to instantiate with a new value." again ... that is not possible – Stultuske Oct 25 '18 at 13:12
  • you cannot add new values to an existing enum. This would break the entire idea behind it of having a predefined list of possibilities. If you want to use different values then supported by the enum that serialize it to its string value and store that instead. – Gerben Jongerius Oct 25 '18 at 13:17
  • I am not discussing what I should do, or not! The resulting bytecodes of the compilation of a enum is a class. In this case it has a constructor. So why the reflection API gives the exception `NoSuchMethodException: Xpto.()` ??? It is not reporting an error related to the case of being a enum, or not. It just says there is no such method. And that is not true. I may rewrite my question to leave it clear. – Miguel Gamboa Oct 25 '18 at 13:22
  • 1
    Read [this answer](https://stackoverflow.com/a/51246456/2838289). It describes how to add a new value to an `enum`. – LuCio Oct 25 '18 at 14:07
  • I especially like this part of the explanation: "On java 9 this might not compile " – Stultuske Oct 26 '18 at 05:52

3 Answers3

4

Here is from Java documentation:

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.

tsolakp
  • 5,858
  • 1
  • 22
  • 28
1

EDIT: Completely changed my answer after some additional checks...

Well, your output of javap -private is weird, and you should validate that it is correct. My output is this:

final class Xpto extends java.lang.Enum{
    public static final Xpto Bar;
    private static final Xpto[] ENUM$VALUES;
    static {};
    private Xpto(java.lang.String, int); // see this line!
    public static Xpto[] values();
    public static Xpto valueOf(java.lang.String);
}

So the constructor really created by the compiler for your enum is a two-arg constructor, taking a String and an int (enum name and value). This is logical, as a name and some kind of numeric identifier are needed for all the cool enum features at run-time.

So, changing your code in this way results in a much "better" error message:

Constructor<Xpto> constructor = Xpto.class.getDeclaredConstructor(String.class, int.class);
constructor.setAccessible(true);
constructor.newInstance("Foo", 2);

Results in:

Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
    at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
Florian Albrecht
  • 2,266
  • 1
  • 20
  • 25
  • I don't see that constructor from javap, but only the parameter less. Here it is my output of javap: https://ibb.co/bQCsBV. I am using JDK 11. However I already compiled it with JDK 8 and got the same output. – Miguel Gamboa Oct 25 '18 at 16:10
  • @MiguelGamboa hmm, that's strange. On my side, it's all Oracle Java 8. Perhaps some change in the javap implementation of JDK 11, which now calculates back to the constructor which is in the code (removing the two auto-parameters)? But that would also be quite strange... Could be basis for a new question :-) – Florian Albrecht Oct 26 '18 at 08:37
-2

because you try to access non static inner class from static method , so its normal