1

I would like to specify a javax constraint annotation message as an enum. Since the javax constraint message needs to be a String, I tried using enum#name() as follows:

import javax.validation.constraints.NotEmpty;

public class Test
{
    enum MessageKey {
        KEY_1,
        KEY_2
    }

    @NotEmpty(message = MessageKey.KEY_1.name())
    private String name;
}

However, this failed to compile:

C:\Users\GeoffAlexander\Documents\Java>javac -cp .\validation-api-2.0.1.Final.jar Test.java Test.java:10: error: element value must be a constant expression @NotEmpty(message = MessageKey.KEY_1.name())

Since enum#name() is declared as final, I expected the compiler to process MessageKey.KEY_1.name() as a constant. But it seems that it does not. Is there any way I can specify the enum constant as the message parameter (I don't need a non-enum workaround as I already know how to do that)?

Geoff Alexander
  • 419
  • 5
  • 13
  • 1
    constant != final. – Turing85 May 02 '22 at 18:23
  • I realize that in general that final is not the same as constant. But a final method means that the method can't be overwritten. Since the compiler has at compile time the information need to generate the String returned by the enum#name() method, I would think that the compiler could simply replace the enum#name() call with a constant String. – Geoff Alexander May 02 '22 at 18:54
  • 2
    The [JLS](https://docs.oracle.com/javase/specs/jls/se17/html/jls-15.html#jls-15.29) is quite explicit on what a constant expression is and what it can contain. Method calls are not allowed. – Turing85 May 02 '22 at 19:06
  • @Turing85 Thanks, that explains why using enum#name() doesn't work. Maybe I'm missing something, but I wonder if the JLS is being overly conservative in not allowing the compiler to treat method calls as constant when it determines that the return value is guaranteed to be constant. But this is really beyond the scope of my original question. – Geoff Alexander May 02 '22 at 19:30

2 Answers2

3

A work-around that we apply occasionally:

Inside that enum class, declare the desired key as String constant:

enum MessageKey {
    KEY_1(KEY_1_STR), KEY_1(KEY_2_STR);

    public static final KEY_1_STR = "KEY_1"; ...

    private final keyAsString;
    private MessageKey(String key) { keyAsString = key; }

    public String getKey() { return keyAsString; }
}

Now you can point your annotation to that STRING constant inside your enum.

Of course, that leaves you open to typos in the string constant itself. That one you can fix with a unit test that automatically checks all enum constants for their key and ensure it matches.

It is not nice, but when you want those two things: A) some enum and B) to use the enum constants as annotation, well: it does that.

(disclaimer: I just wrote down the above without running it through the compiler, consider it pseudo code that gives the idea, not a 100% correct implementation)

GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • Thanks. That's the workaround I'm already using. – Geoff Alexander May 02 '22 at 18:56
  • 1
    @GeoffAlexander Well, you just said you had *one* workaround. And there might always be a future reader coming along with the same problem, who might be happy about this work around. And not everybody remembers that one can write a nice unit test that ensures consistency. – GhostCat May 02 '22 at 19:00
0

No you cannot.

The only options available to you are non-enum constants, or some other workaround. In Java, a constant expression is the only permitted value of an annotation element. Here is the definition of a constant expression, but in short, a constant expression is only allowed to return primitives (int, char, etc.) or a String, and the expression itself has limits to what operations you can perform within.

Since enums are Objects and are not primitives or a String, they already break the first rule. And even if you tried to use Enum.name(), there is a list of rules that decides what is allowed in an expression, and Objects are not permitted. Therefore, you are blocked on both fronts.

As of May 2022, what you want is impossible to do in Java.

However, there is a decent chance that this changes in the future. We are/might be getting new primitive types and value objects. I imagine that when/if we get these, the rules for constant expressions may change.

davidalayachew
  • 1,279
  • 1
  • 11
  • 22