Assigning an @Annotation enum a value

41,539

Solution 1

You can do it like this:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

class Person {    
    @Presentable({
        @Restriction(type = RestrictionType.LENGTH, value = 5),
        @Restriction(type = RestrictionType.FRACTION_DIGIT, value = 2)
    })
    public String name;
}

enum RestrictionType {
    NONE, LENGTH, FRACTION_DIGIT;
}

@Retention(RetentionPolicy.RUNTIME)
@interface Restriction {
    //The below fixes the compile error by changing type from String to RestrictionType
    RestrictionType type() default RestrictionType.NONE;
    int value() default 0;
}

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@interface Presentable {
  Restriction[] value();
}

Solution 2

A part from compilation error, suppose you are able to do exactly this. Then don't you think applying the similar annotation on some other field will ruin the first one?

I mean to say,

@Presentable(restrictions=Restrictions.maxLength.setValue(64))
public String userName;
@Presentable(restrictions=Restrictions.maxLength.setValue(32))
public String password;

The same instance will now have a different value, that is 32. So, the 64 will be lost, I believe. In case, they are processed on runtime sequentially, and at the time we change the value to 32, 64 one has already been processed. Then I suppose, we can change the setter method in the example given by mdma to something like below.

 static public Restriction setValue(int value) {    
      this.value = value;
      return this;
  }

Solution 3

You can achieve what you want, but not with enums directly.

If you make Restriction a regular class, with private constructor and static constant fields, you can then use method chaining to create new instances fluently:

enum RestrictionType
{
   none,
   enumeration,
   maximumLength, 
   // ... etc.
}

class Restriction {
  static public final Restriction none = new Restriction(RestrictionType.none);
  static public final Restriction enumeration = new Restriction(RestrictionType.enumeration);
  static public final Restriction maximumLength = new Restriction(RestrictionType.maximumLength);

  ... etc

  RestrictionType type;
  int value;

  private Restriction(RestrictionType type)
  {
    this(type, 0);
  }
  private Restriction(RestrictionType type, int value)
  {
     this.type = type;
     this.value = value; // you don't really need
  }

  static public Restriction setValue(int value)
  {
      return new Restriction(type, value);
  }
}

Which is then used exactly as your original code:

@Presentable(restrictions=Restrictions.maxLength.setValue(64))
public String userName;

However, I'm concerned for the lack of OO here - if the restrictions have different behaviour or data needed for definition, then you will end up lumping everything in the Restrictions class. It will be better to create subclasses for the different restriction types.

Solution 4

I chose Abhin's as the answer to my question because it was the most comprehensive and it worked when I tried it out. However, I document here, in the form of an answer to my own question, what I actually did.

Renaming Abhin's terms, this would be how I would apply it (similar to Abhin's example):

@Presentable({
@Restrictions(restriction=Restriction.FractionDigits, value="1"),
@Restrictions(restriction=Restriction.Length, value="10"),
    .....
})

Which I decided is too verbose. I could even abridge it to:

@Presentable({
@R(r=R.FractionDigits, v="1"),
@R(r=R.Length, v="10"),
    .....
})

Which might be too incomprehensible and still to verbose. What I needed was something a programmer could specify quickly and comprehensively:

@Presentable(sequence = 11, maxLen=64, readOnly=true)

Therefore, I decided to use the quick and dirty:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
public @interface Presentable {
    int sequence();
    String caption() default "";
    int fractionDigits() default -1;
    int length() default -1;
    int maxLen() default -1;
    int minLen() default -1;
    int totalDigits() default -1;
    float maxVal() default -1;
    float minVal() default -1;
    String pattern() default "";
    String whiteSpace() default "";
    boolean readOnly() default false;
    boolean multiValue() default false;
    boolean hidden() default false;
    boolean isTest() default true;
}

Anyway, I am keeping Abhin's answer in my recesses for future use.

Share:
41,539
Blessed Geek
Author by

Blessed Geek

מבורכים החנונים

Updated on May 08, 2020

Comments

  • Blessed Geek
    Blessed Geek about 4 years

    I created

    enum Restrictions{
      none,
      enumeration,
      fractionDigits,
      length,
      maxExclusive,
      maxInclusive,
      maxLength,
      minExclusive,
      minInclusive,
      minLength,
      pattern,
      totalDigits,
      whiteSpace;
    
      public Restrictions setValue(int value){
        this.value = value;
        return this;
      }
      public int value;
    }
    

    So that I could happily do something like this, which is perfectly legal syntax.

    Restrictions r1 =
      Restrictions.maxLength.setValue(64);
    

    The reason being is, I am using enum to restrict the type of restriction that could be used, and be able to assign a value to that restriction.

    However, my actual motivation is to use that restriction in an @annotation.

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
    public @interface Presentable {
      Restrictions[] restrictions() default Restrictions.none;
    }
    

    So that, I intended to do this:

    @Presentable(restrictions=Restrictions.maxLength.setValue(64))
    public String userName;
    

    to which, the compiler croaks

    The value for annotation enum attribute must be an enum constant expression.
    

    Is there a way to accomplish what I wish to accomplish

  • Adeel Ansari
    Adeel Ansari about 14 years
    Dont' you think you are creating a new instance on every invocation of set method?
  • Blessed Geek
    Blessed Geek about 14 years
    public @interface Presentable {Restriction[] restrictions() default Restriction.none;} results in compiler croaking "only primitive types, String, Class, annotation or enum, or 1 dim arrays thereof, are permitted."
  • mdma
    mdma about 14 years
    @h2g2java Make Restriction an annotation type.
  • mdma
    mdma about 14 years
    @Adeel - yes this creates a new instance for each annotation. The annotations have state, and so separate instances are required. How could you do it without creating a new instance?
  • Adeel Ansari
    Adeel Ansari about 14 years
    I agree that its having state.
  • Adeel Ansari
    Adeel Ansari about 14 years
    Indeed, a better approach. Couldn't see this in the first place, while thinking along the same line. +1
  • bennidi
    bennidi almost 10 years
    Correct! Enums are constant objects (compiled to final classes) such that each enum value is a singleton. Enums should never be mutable objects, IMHO.