Win a copy of TDD for a Shopping Website LiveProject this week in the Testing forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Paul Clapham
  • Ron McLeod
  • Jeanne Boyarsky
  • Tim Cooke
Sheriffs:
  • Liutauras Vilda
  • paul wheaton
  • Henry Wong
Saloon Keepers:
  • Tim Moores
  • Tim Holloway
  • Stephan van Hulst
  • Carey Brown
  • Frits Walraven
Bartenders:
  • Piet Souris
  • Himai Minh

dynamic enum as method parameter

 
Ranch Hand
Posts: 31
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hi,

I am breaking my head around this problem in which I need general code to use different enums.

At runtime I want to direct wich list to check by passing the enum name as method parameter ("Relatives" or "Connections").

The enums implement an interface that has a method List<String> getOptionTypes() to check if they contain the value.


I made a unit test to clarify my intentions, and that works when I use a defined enum instead of the desired dynamic part:




Anybody having an idea to tackle this in a simple way?
 
Bartender
Posts: 4911
185
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
hi Kenji,

no, I could not think of a way to get the enum, given its name. But then again, do you need those two enums? Why not simply combine the two to just one enum?

While keeping the two enums, I tried to come up with a static Map<String, String> in the EnumOptionsTest class:

And I filled it with:
and likewise for the Connections enum. And in the test at the end, I have:

 
Kenji Watanabe
Ranch Hand
Posts: 31
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hi Piet, thank you, a Map could be a solution indeed.
Only it has to be passed in as a parameter, because the collection of values to check against may differ.
So one time it will be values representing Relatives, and in another scenario it will be Connections.
 
Piet Souris
Bartender
Posts: 4911
185
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Right. Well, one more attempt then:

I added this static method to your test class:
}
and in the main method I have this code to test:
 
Sheriff
Posts: 16930
284
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I not clear on what you're trying to achieve. It seems your intent is something like this:

and you'd like to know how to implement a method like toEnums()?
 
Kenji Watanabe
Ranch Hand
Posts: 31
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thank you guys for your help.
I guess the answer should be that you do not use enum classes to pass as parameters.
I think I'd keep the readability in one enum class, and have the calling method agnostic.
The enum constants are actually a fixed list so they can be joined indeed and the calling method should receive a subset based on the parameter value.
The calling method is ofcourse now stubbed in the test:


What I do not like is that I have to use a Map but I guess there is no other way.
 
Junilu Lacar
Sheriff
Posts: 16930
284
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Kenji Watanabe wrote:What I do not like is that I have to use a Map but I guess there is no other way.


I'm pretty sure there's a better way; I'm just not sure what exactly you're trying to do -- it's still unclear to me from the test you wrote.
 
Junilu Lacar
Sheriff
Posts: 16930
284
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Here are some observations about the enum class design your test code suggests:

The API seems more focused on Strings rather than the OptionFilters type itself:

Why would I expect a List<String> from OptionsFilter.getList(String) instead of a List<OptionsFilter>? Seems like you're missing out on one of the points of using an enum class: to have a type-safe way of dealing with a limited set of values. A List<String> is far less limited than a List<OptionsFilter>.

This expectation is particularly baffling to me in the context of a enum type:

Why??? So you're expecting the list to be in a specific order? How would anyone who has not seen the implementation know this? Is this a white box test? Why would the order of items returned be of any concern?

In fact, there is no variable in the testSubselection() test that is declared as an OptionsFilter value. Everything is a String or List<String>. Why even use an enum then?

This really highlights your focus on Strings and List of Strings:

Why private? Why static? Why not have this instead:

The fact that you made this method private static tells me your intent was solely to use it in service of the private getList(String[]) method.

What I see is a lot of unnecessary jumping through hoops to get something out of the OptionsFilter enum class that you shouldn't be expecting to get out of it. These are mis-assigned responsibilities.

Give me some time and I'll suggest a more enum-focused API.
 
Kenji Watanabe
Ranch Hand
Posts: 31
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hi Junilu,
I must admit having little experience working with enums, but my intention is to have readability instead of numbers.
I really would like to use the strict typing of enums so I guess I must pursue a List<OptionsFilter>.
The assertion of the value was simply to be sure the code works, and had no functional meaning. Ofcourse it should be tested with an OptionsFilter constant.
 
Junilu Lacar
Sheriff
Posts: 16930
284
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
So here's the intent that I've gleaned from the test code:
Given a list of string "codes", I want to get a list of those codes that correspond to (either "relatives" or "connections")

First of all, I'd modify that to:
Given a list of string "codes", I want a list of corresponding OptionFilter enums that are (either "relatives" or "connections")

So the new intent, an API for an enum like OptionsFilter might be used like so:

To me, this API would set me down a path to assigning responsibilities in a much better way.
 
Junilu Lacar
Sheriff
Posts: 16930
284
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Kenji Watanabe wrote:but my intention is to have readability instead of numbers.


If that is your intent, why would you have this assertion:

instead of this:

Isn't the latter form more readable?
 
Piet Souris
Bartender
Posts: 4911
185
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I was also trying to get a simpler way. First, I added another field to the enums, like:
I renamed some methods (like: getList<String[]>(names) to getListOfValues(List<String> names)
(where the difficult part is remembering that 'name' stands for the name of an enum value)
And my main is now (not bothered by the tests)

 
Kenji Watanabe
Ranch Hand
Posts: 31
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
@Junilu
As I wrote the testing of the value was just to be sure the code worked but it should be like your version.
I do think your solution is what I am searching for. Only I do not understand how the predicate gets set by getPredicate(subset) for each option. Could you explain how you intend to use that?
 
Kenji Watanabe
Ranch Hand
Posts: 31
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
@Piet
Your solution works for the fixed configuration of these two subsets, Connections and Relatives. I was also looking for a way to add easily a new subset of values, like Acquintances or Clients f.e. with no further impact on the design.


By all means, thanks to both of you!
 
Junilu Lacar
Sheriff
Posts: 16930
284
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 2
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Kenji Watanabe wrote:Only I do not understand how the predicate gets set by getPredicate(subset) for each option. Could you explain how you intend to use that?


There are several ways you can go about this and still retain type-safety. Let's start with the simple way first.

If the subsets are not going to be very volatile, then you could define them as constant lists:
 
Junilu Lacar
Sheriff
Posts: 16930
284
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Another way is to define your subsets themselves as enums and take a similar approach as that shown by Piet:

You can then do this:

In contrast to what you had earlier, this code works primarily with enum values rather than strings.
 
Kenji Watanabe
Ranch Hand
Posts: 31
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I think I'll go for the first version because of the requirement to make up new subsets. Thank you for the elaboration.
 
Junilu Lacar
Sheriff
Posts: 16930
284
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
You can run the live demo here: https://repl.it/@jlacar/EnumSubsetDemo
 
Junilu Lacar
Sheriff
Posts: 16930
284
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Kenji Watanabe wrote:I think I'll go for the first version because of the requirement to make up new subsets. Thank you for the elaboration.


Both ways require code changes if you're going to add new enum values. If you're just defining new subsets of the same set of enum values, then the first way I showed is a little bit more flexible because you can always inject the subsets you want to define through Spring, for example, without changing any code.
 
Kenji Watanabe
Ranch Hand
Posts: 31
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hi Junilu, cool, works like a charm. But yes, defining new subsets of the same set of enum values, so the first way is preferred. Thanks again!
 
Junilu Lacar
Sheriff
Posts: 16930
284
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
One last thought.

In that getPredicate(String subsetName) method, I had this:

The default of giving back all the enum values for an invalid subset name is one design choice. Depending on the semantics you want to create, it could also be more appropriate to return an empty list for an invalid subset name. It just depends on what makes sense in the most common usage scenario.
 
What? What, what, what? What what tiny ad:
Free, earth friendly heat - from the CodeRanch trailboss
https://www.kickstarter.com/projects/paulwheaton/free-heat
reply
    Bookmark Topic Watch Topic
  • New Topic