6

I'm using Spring to resolve property values from properties file, usually with @Value("${my.property}").

Now I have an enum that should have an application-wide configurable static number. For example:

public enum PersonType {
    ADULT, CHILD;
    private static final int MAX_CHILD = 17;

    public static PersonType fromAge(int age) {
        return age <= MAX_CHILD ? CHILD : ADULT;
    }
}

How could I make the max child age configurable and injectable by Spring?

membersound
  • 81,582
  • 193
  • 585
  • 1,120
  • 1
    something similar has been asked before - http://stackoverflow.com/questions/23222468/using-java-spring-injection-with-public-static-final-objects-for-jakarta-unst. short answer is you cant. long answer is you can, with some really ugly reflection, but spring wont do it. you could write a @ Component that will inject your value into some field and set the static final field using reflection in @ PostConstruct – radai Jul 04 '14 at 09:48
  • That's a great idea to create a post-initialization component. I'll probably go for it, as I'd prefer not to rely on reflections.- – membersound Jul 04 '14 at 09:55
  • IMHO a better way to proceed is to move out `PersonType.fromAge` in `PersonUtils` as `fromAge(int age,int maxAge)` and use spEL and property injection.
    This is a my opinion, but put utility code into enum is not a good pratice; `enum`s are designed to contains values, not to have code you can refactor in a lot of other ways
    – Luca Basso Ricci Jul 04 '14 at 09:57
  • so long as that field remains static final reflection is your only option (as its initialized at class-load time). moving it to a static method in a utility class, as @LucaBassoRicci suggests, its the cleaner option – radai Jul 04 '14 at 09:58

2 Answers2

8

It's an interesting question, how one handles variables that is the same for all objects of the class and does not change during runtime, and at the same time is allowed to be configurable between executions. Since the first two prerequisites dictate that the variable should be static and final (e.g. a constant), the third really doesn't fit in, meaning that there will be no pretty way to achieve all three (reflection is needed, or one has to drop either the static or the final constraint).

Since there is no pretty solution to the way things are modeled now, I think the wisest would be to take a step back and rethink the placement of the variable: Is it necessary to keep this logic in the enum itself? What is different when changing the value of the constant, the enum itself, or something else? In what cases does this constant have to change it's value?

In your example it might be that different countries have different thresholds for what is regarded as adult, or that the threshold changes, then maybe a small service that determines which PersonType a Person has is the right way to go.

@Service
public class PersonTypeService {
    @Value("${threshold.for.adulthood}")
    private int thresholdForAdulthood;

    public PersonType determinePersonType(final Person person) {
        if (person.getAge() >= thresholdForAdulthood) {
            return PersonType.ADULT;
        }
        return PersonType.CHILD;
    }  
}

In general I like to let enums only answer the "what", and leave the "how" and the "why" to domain classes and services. In the example, all the enum needs to know is the values it provides a person, why it should provide a certain value, or how it is determined, does not belong in the enum.

Tobb
  • 11,850
  • 6
  • 52
  • 77
0

Moving the logic to get proper enum based on configurable age can be one of the solution

class PersonTypeFinder
{
    private int maxChildAge; // set this from spring
    ....

   public PersonType getPersonType(int age)
   {
     return age <= maxChildAge ? PersonType.CHILD : PersonType.ADULT;
   }
}

enum PersonType 
{
   ADULT, CHILD;
}
vinayknl
  • 1,242
  • 8
  • 18