Win a copy of Spring Boot in Practice this week in the Spring 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
  • Tim Cooke
  • Ron McLeod
  • Jeanne Boyarsky
  • Paul Clapham
Sheriffs:
  • Liutauras Vilda
  • Henry Wong
  • Devaka Cooray
Saloon Keepers:
  • Tim Moores
  • Stephan van Hulst
  • Tim Holloway
  • Al Hobbs
  • Carey Brown
Bartenders:
  • Piet Souris
  • Mikalai Zaikin
  • Himai Minh

Casting an Enum for use with an Overloaded Method

 
Ranch Hand
Posts: 262
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Under normal circumstances, I can answer this question myself. My problem is I am updating existing code and am limited in what changes I can make. The two key points are-

I have a class with several overloaded methods that take an enum as an argument. The current code has lines like-


All fine in the current code.

The code has another class for building part of the interface and it takes arrays, one per column. The array size is the number of rows. An example is passing in entry components.



After (many) years a new requirement is to associate an enum with each row. That would be fine if we were talking different values of the same enum, but different rows have different enums associated with them. The need is something like this.



I understand that I am dealing with what Java knows at compile time versus run time, i.e. at compile time the array elements are just Object. These statements work-


presumably because the class is resolved at runtime in these statements.

That allows me to write a big if else clause covering all possible enums (and have to add to it whenever a new enum is defined). I was hoping to find a more elegant solution (within the constraint I need to call the existing overloaded methods). I have more flexibility on passing in the array- just that the association to the other arrays used in the interface is by array index.

I thought I  would ask for comments before committing to something that might be pretty kludgy.
 
Marshal
Posts: 27289
87
Eclipse IDE Firefox Browser MySQL Database
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
You can have an enum implement an interface. So all of your enums could implement a specific interface which declares all of the methods that they have in common, and then have your overloaded methods take the interface as their parameter instead of the enum. Like this:



I'm assuming that your enums have methods which will be called inside the doSomething(foo) methods. Hopefully they can be adapted to all have the same methods so that they can implement an interface with those methods, otherwise you'll be stuck with casting.

In my case I had two kinds of enum which were built based on different parameters (one group was based on a distance and the other was based on start and end locations), and they could both be made to act in the same way.
 
Saloon Keeper
Posts: 9434
79
Eclipse IDE Firefox Browser MySQL Database VI Editor Java Windows
 
Jon Swanson
Ranch Hand
Posts: 262
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I think you have hit on my primary problem.

There is a class, which I cannot modify, that contains methods that are overloaded. The behavior of the method depends on the enum that is passed to the method. I cannot move these methods into the enum, they rely on a lot of data structures and other methods of the class they are currently in.

I tried writing an interface that returned the enum. That approach did not work.








Still gets Object as the class of the enum

I would have to call the external method from inside the enum and pass it the value of the enum (with  the appropriate type) and I do not know how to do that.


 
Paul Clapham
Marshal
Posts: 27289
87
Eclipse IDE Firefox Browser MySQL Database
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Jon Swanson wrote:There is a class, which I cannot modify, that contains methods that are overloaded. The behavior of the method depends on the enum that is passed to the method. I cannot move these methods into the enum, they rely on a lot of data structures and other methods of the class they are currently in.



That code you posted, couldn't you just replace it by a call to Enum.valueOf()?

Otherwise if the unmodifiable class has methods which expect to be passed instances of enum ONE or TWO then I don't see that it's possible to do anything to the enums anyway.
 
Paul Clapham
Marshal
Posts: 27289
87
Eclipse IDE Firefox Browser MySQL Database
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Paul Clapham wrote:That code you posted, couldn't you just replace it by a call to Enum.valueOf()?



Actually after reading the API docs for that method I see that every enum E has an implicit "public E valueOf(String)" which is the code you have there. (Except it doesn't like nulls.)
 
Rancher
Posts: 4252
57
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
There are a few more differences than that.  The valueOf() method does not accept nulls, but also throws an exception if an exact match is not found, and does not ignore case when looking for a match.  So, it can be reasonable to write an alternate version of valueOf() that is more tolerant of such issues.
 
Jon Swanson
Ranch Hand
Posts: 262
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I think the discussion digressed a bit. I have code that uses overloaded functions which take different enum classes that (in part) determine what the method returns.

I have another class expecting arrays to assign instances to rows.

If try to use a generic Enum[] then the methods see the class Object. In the past the methods were only used with specific enums and there is a lot of existing code making use of them.

The array is a new request. I'll try valueOf(), but my function, since it requires implementing an interface, also results in the overloaded functions seeing a type of Object for the enum.

I can explicitly cast the enums from the array, but that removes the generic nature of using an array and deciding what to do at run time. I can also use something like instanceof and have a big if else block. I was hoping to find something cleaner.
 
Jon Swanson
Ranch Hand
Posts: 262
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Follow-up using valueOf



This encapsulates the problem- by placing  the enum in the array, its type is not known at compile time and valueOf() fails to compile. Presumably for the same reason the overloaded methods fail.

method valueOf in class Enum<E> cannot be applied to given types;
     unitPrint( myEnums[ 1 ].valueOf( myEnums[ 1 ].toString() ) );
                            ^
 required: Class<T>,String
 found: String
 reason: cannot infer type-variable(s) T
   (actual and formal argument lists differ in length)
 where T,E are type-variables:
   T extends Enum<T> declared in method <T>valueOf(Class<T>,String)
   E extends Enum<E> declared in class Enum

If I switch to an array of Objects-

error: cannot find symbol
     unitPrint( myEnums[ 1 ].valueOf( myEnums[ 1 ].toString() ) );
                            ^
 symbol:   method valueOf(String)
 location: class Object
1 error

I have not found a way to pass an enum into an overloaded method when the enum comes from an array containing different types of enums except by explicitly casting the enum in the method call, which defeats the purpose of having an overloaded method.
 
Paul Clapham
Marshal
Posts: 27289
87
Eclipse IDE Firefox Browser MySQL Database
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
No, if you can't unify the enum classes into a single entity then I don't think you have any hope of fixing that problem. It seems that it's important for the enum values to know which enum they come from, so unifying them via a common interface wouldn't be helpful.
 
Mike Simmons
Rancher
Posts: 4252
57
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I'm struggling to understand what you need to know the class of the different enums for.  Can you give a more specific, useful example?  The one you gave is

where it seems that the goal is to convert an enum to a String and back to the same enum.  I can make that work with something like this:
 
But why not just use this?

This goes back to my original question, it's not clear what you would want to do, knowing the class of the enum.

However, one possibility that occurs to me is something like this:

Say we have some unrelated enum classes:

Note that they have methods, but no common interface, and not even the same names for the methods.  Yet, I am assuming there is some context where, in some situation, you might want to call one set of methods, and in another situation, you might want to call a different set of methods.  For my example, the two situations are "opening" and "closing" something.  But we are stuck with badly-designed enums that did not implement a common interface, so there is no standard open() or close() method.  But there are some class-specific methods we might want to call instead, without being able to edit the original enums.

I think your original problem description suggest you might be trying something like this, using overloaded open(Color) and open(Shape) methods:

One way you might transform this, without editing the original enums, is to use Maps rather than a succession of if/else instanceof checks.  

Now, this might not look any better to you.  It may well look more complex.  You do still have to insert casts at some point.  I don't see a way around that, if you need to put different enum classes in a shared list or array, and they don't have a shared common type other than Enum.  The main advantage of this approach, from my perspective, is that using a Map that uses the Class as key allows you to get rid of the line of if/else instanceof checks.  The Map can be more efficient and concise, as the number of different enums and methods increases.

Future Java versions have a more elegant notation for this sort of thing - it's a preview feature in JDK 17 and 18, Pattern Matching for Switch.  Basically it lets you replace the Map with a switch statement, and you get to avoid explicitly casting because once you're in a particular case branch, it's already clear what the type is.
 
Jon Swanson
Ranch Hand
Posts: 262
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Mike,

Thanks for the detailed and thoughtful answer.  The situation I have is slightly different, but very similar to what you discussed.

I have a class with a set of overloaded methods.



That had been working fine, in the sense that different portions of the code needed similar function, but did not overlap.



That sort of operation is handled without a problem. Where I get the problem is with a new requirement for old code.



I can't use overloading because the open method sees the type passed from the Enum array as Object in every case,



Thanks for that, this compiles. I was trying to trick Java into returning something at runtime that would actually be seen as Color or Shape. Nope, still seen as Object.

So I was thinking I would be stuck with creating a method-

static void open(Object anEnum) {
  if (anEnum instanceof Color) open((Color) anEnum);
  if (anEnum instanceof Shape) open((Shape) anEnum);
}

which is a kludge to keep from making more extensive changes to the code, and works, but seems to go against the reason for overloading in the first place.

I like the MAP idea. I am a little concerned it will run afoul of the same problems I have been having. The methods are in a class focused on carrying out a specific big messy calculation. Exactly how the calculation is carried out depends on which exact "class" of problem is being worked and the exact kind of class. For better or worse, long ago there was one class of problem and an enum created for the kind of "class." Much later, almost the same methods were needed in another class of problem and a second enum was created with its kinds of "class." The top level functions that did the messy bits were overloaded. All was fine until now when there was a request to send the enums in as an array and handle multiple messy bits at  once (as a change to a class that did everything with arrays but until now, did nothing related to the messy bits).


Thanks to everyone. I think the advice will be really helpful going forward to avoid the problem I have fallen into. In the meantime, I am feeling a little better about what I need to do to patch this code to keep it going a bit longer (hopefully some refactoring will be scheduled in at some point).
 
Marshal
Posts: 76111
362
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Jon Swanson wrote:. . . . . .

Why not use an array initialiser?I think you have to give a name to the array before it will compile. There is something iffy about non‑private fields, particularly those whose type is some sort of array.
 
Jon Swanson
Ranch Hand
Posts: 262
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Yes, a typo-



Yes, initializing the array when it is created is cleaner. I believe in the actual code the array is used more than once, though.

In any case, the last line doesn't work. The open method is overloaded, but gets the type Object rather than Color or Shape.
 
Mike Simmons
Rancher
Posts: 4252
57
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Are you sure it sees an Object, and not a java.lang.Enum, at least?  That doesn't really seem to make sense.  I understand you can't see the specific type of enum - but the type of the array should be Enum[], at least.  You should be able to see a few standard enum methods like name() and ordinal().

Now there's further confusion because you're saying it's a myEnum[], which seems impossible since java enums can't have any common type except for Object, Enum, or a common interface - which you have said is not the case here.  So I assume this is mock code that has never run.  That would also explain the array initializer that works without a "new" keyword. ;)

(Also if you actually have a class name "myEnum" that drives many of us crazy due to misleading with nonstandard naming conventions.  But it could at least compile.)
 
Jon Swanson
Ranch Hand
Posts: 262
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
What it sees at compile time and runtime are different.

I added additional overloaded functions to see what I would get.



With this code-



It prints "Weight"

With this code-



It prints "Object"

That is not to say that I cannot have this method-



That prints "WEIGHT" when I open( w[0] ) since it is resolved at runtime

I can also do this-



and it prints "WEIGHT" when I open w[0].

The latter examples are why I have been pounding my head against the idea of getting the overloaded methods to work.

Sorry for the typos- been doing jury duty so have to respond before or after.
 
Mike Simmons
Rancher
Posts: 4252
57
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Try adding

You should see that the type seen by the compiler is Enum, not just Object.
 
Jon Swanson
Ranch Hand
Posts: 262
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Mike,

Sorry. In what I am compiling I tried



and



I could assign



as an element of either array, and in neither case could I use the overloaded methods.

However, you are right, that I could used these overloaded methods



the first method would run for the Enum[] array and the second method would run for the Object[] array.

In either case, it still seems like I need to have a bunch of if (e instanceof) statements and then do explicit casts to the type of enum. That works whether I assign the enums to Object[] or Enum[].
 
Carey Brown
Saloon Keeper
Posts: 9434
79
Eclipse IDE Firefox Browser MySQL Database VI Editor Java Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Seems like a maintenance nightmare that you're leaving behind for your successor.
 
Jon Swanson
Ranch Hand
Posts: 262
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Carey,

Maybe. But I don't have the go ahead to rewrite the existing overloaded methods. These methods are not simple, they involve thousands of lines of code (starting in the class containing the overloaded methods). At least the additional overloaded method will be in the same class with the other methods of the same name and will do nothing more than call the "expected" method after casting the enum to the correct type based on the enum itself.

All the enums are defined in a single static class. Not too many places to look.

And this way none of the code that assumed that one can just pass in an enum for a specific method and get the expected result will break.
 
Consider Paul's rocket mass heater.
reply
    Bookmark Topic Watch Topic
  • New Topic