Java Forum / General / March 2007
enum and switch
Wojtek - 07 Mar 2007 16:32 GMT From the compiler complaints I guess this is not possible, yet....
---------------------------- public class MyClass { public enum Things { ONE,TWO }
public boolean process(Things thing) { boolean success = false;
switch ( thing ) { case Things.ONE: success = doStuff(1); break;
case Things.TWO: success = doStuff(2); break; }
return success; } ------------------------------
Is there a way to use an enum in a switch statement?
 Signature Wojtek :-)
Daniel Dyer - 07 Mar 2007 16:39 GMT > From the compiler complaints I guess this is not possible, yet.... > [quoted text clipped - 26 lines] > > Is there a way to use an enum in a switch statement? You don't need to qualify the enum constants. In fact, you are only allowed to use unqualified names. So it should be:
switch (thing) { case ONE: // Do something. case TWO: // Do something else. }
Dan.
 Signature Daniel Dyer http://www.uncommons.org
Wojtek - 07 Mar 2007 17:36 GMT Daniel Dyer wrote :
>> From the compiler complaints I guess this is not possible, yet.... >> [quoted text clipped - 39 lines] > > Dan. OK, but what if the enum is from another class? I must then qualify it.
BTW I ended up using an if/else if tree, though I would rather use a switch/case.
 Signature Wojtek :-)
Daniel Dyer - 07 Mar 2007 18:06 GMT >>> Is there a way to use an enum in a switch statement? >> [quoted text clipped - 15 lines] > BTW I ended up using an if/else if tree, though I would rather use a > switch/case. Did you try it with the switch statement and the unqualified labels? The compiler knows what class the enum constants are from because it knows the type of the argument to the switch statement.
Dan.
 Signature Daniel Dyer http://www.uncommons.org
Wojtek - 07 Mar 2007 18:40 GMT Daniel Dyer wrote :
> Did you try it with the switch statement and the unqualified labels? The > compiler knows what class the enum constants are from because it knows the > type of the argument to the switch statement. It works.
I would not have thought of this.....
Thanks!
 Signature Wojtek :-)
Steve W. Jackson - 07 Mar 2007 19:05 GMT > Daniel Dyer wrote : > [quoted text clipped - 7 lines] > > Thanks! A more careful examination of the compiler error would actually tell you this. I learned it the hard way after adopting a practice in an ongoing project of *always* qualifying our enum references. The compiler squawked and I had to read it several times to fully grasp that it was telling me I could not do so.
= Steve =
 Signature Steve W. Jackson Montgomery, Alabama
Wojtek - 07 Mar 2007 19:16 GMT Steve W. Jackson wrote :
> A more careful examination of the compiler error would actually tell you > this. I learned it the hard way after adopting a practice in an ongoing > project of *always* qualifying our enum references. The compiler > squawked and I had to read it several times to fully grasp that it was > telling me I could not do so. You are right of course. It is just that I am so used to qualifying things, and along comes this new "feature".
The last thing I would have expected is that the compiler would qualify it for me AND cause an error if I did.
Fully qualifying the constant should have produced a warning about an unnecessary cast. IMHO of course :-)
 Signature Wojtek :-)
Tor Iver Wilhelmsen - 08 Mar 2007 22:49 GMT På Wed, 07 Mar 2007 20:16:36 +0100, skrev Wojtek <nowhere@a.com>:
> Fully qualifying the constant should have produced a warning about an > unnecessary cast. IMHO of course :-) Casting does not enter into the picture: Since "case foo:" can only take a constant primitive expression as foo, the compiler obtains the literal value of the enum and puts it into the method code.
Daniel Pitts - 07 Mar 2007 18:45 GMT > >>> Is there a way to use an enum in a switch statement? > [quoted text clipped - 24 lines] > -- > Daniel Dyerhttp://www.uncommons.org Or, even better... Ever hear of Polymorphism?
// Look ma! No Switches! public class MyClass { public enum Things { ONE { public boolean doStuff(MyClass myClass) { return myClass.doStuff(1); } }, TWO { public boolean doStuff(MyClass myClass) { return myClass.doStuff(2); } } ; public abstract boolean doStuff(MyClass myClass);
}
public boolean process(Things thing) { return thing.doStuff(this); }
private boolean doStuff(int i) { return false; } }
Wojtek - 07 Mar 2007 19:11 GMT Daniel Pitts wrote :
>>>>> Is there a way to use an enum in a switch statement? >>>> You don't need to qualify the enum constants. In fact, you are only [quoted text clipped - 50 lines] > } > } Well, ok, except that there is more happening in each case than a method call. And I need to take the time to sit down and actually READ about Java 1.5+
Sigh, not enough hours in each day, and those damn deadlines.....
 Signature Wojtek :-)
Daniel Pitts - 08 Mar 2007 00:00 GMT > Daniel Pitts wrote : > [quoted text clipped - 61 lines] > -- > Wojtek :-) The point is to move the change in behavior out of the switch statement and into the object you are switching on.
public enum Things { ONE { public boolean doStuff(MyClass myClass) { // Do lots of stuff to myClass //... } }, TWO { public boolean doStuff(MyClass myClass) { // Do lots of stuff other stuff to myClass //... } } ; public abstract boolean doStuff(MyClass myClass); }
This is called the State/Strategy pattern. You'll find that well designed OO programs tend NOT to have switch statements. Especially switch statements which switch on the same object in more than one location. Just put a method on the object, and override it for the different cases.
Andreas Leitgeb - 08 Mar 2007 12:49 GMT On Mar 7, 11:11 am, Wojtek <nowh...@a.com> wrote:
> ..., and those damn deadlines..... So, you don't like the swooshing sound as they pass by ???
> This is called the State/Strategy pattern. You'll find that well > designed OO programs tend NOT to have switch statements. Especially > switch statements which switch on the same object in more than one > location. I'd say it mostly depends on how tightly related the switch body is to the current class/method it is placed in.
Tearing apart logical units of code into separate classes is also a good recipe for preventing reliably any future attempt to understand what the code is actually doing.
My suggestion is to have a enum's method return the specific parameters that allow the switch-arms to be united. Where this is considered unfeasible, stick to switch.
Don't put any logic into an enum, that is not mainly related to that enum.
Wojtek - 08 Mar 2007 14:38 GMT Daniel Pitts wrote :
> The point is to move the change in behavior out of the switch > statement and into the object you are switching on. [quoted text clipped - 21 lines] > location. Just put a method on the object, and override it for the > different cases. So you are telling me that the enum (which is in another class) must know about the business logic being evaluated?
The enum I am using is in a class which holds filter criteria. An array of these filters is passed into a file reader. The file reader reads in a line, then for each filter in the array evaluates the line. If the line meets the conditions then it is placed into a collection, to be written out to a Web page.
The filter array is built up in the business logic class of a use case. The filter criteria is created by the user via a servlet. There is a method which iterates through the filters and builds up a human readable display of the filter criteria. With I18N conversion. To be displayed at the top of the page which displays the results.
So I have this enum in two switch statements in two different places. Putting both sets of business logic in the enum seems very wrong to me. Even with overriding of methods, I would still need to have both method signatures in the enum. Which means that the enum would still need to know about what it is being used for. Which breaks use case separation.
And if the enum is in a library, then the library would need to be modified each time the enum is used in a new place.
After all, an enum is just a way of gathering state criteria in a convenient package (type). This can then be used as a parameter which allows the compiler to ensure type safety.
I do not know about other OO languages, but the Java enum definition which allows methods to be attached to enum elements is completely new to me.
 Signature Test Sig
Wojtek - 08 Mar 2007 17:41 GMT Wojtek wrote :
> I do not know about other OO languages, but the Java enum definition which > allows methods to be attached to enum elements is completely new to me. Ok, I can see having characteristics in the enum element.
In fact I am going to put the language key and a method getLanguageKey() as part of each element.
But I do not think that behaviour should be there.
 Signature Test Sig
John W. Kennedy - 08 Mar 2007 18:08 GMT > I do not know about other OO languages, but the Java enum definition > which allows methods to be attached to enum elements is completely new > to me. It was a natural result of Java's everything-is-an-object philosophy.
 Signature John W. Kennedy "The blind rulers of Logres Nourished the land on a fallacy of rational virtue." -- Charles Williams. "Taliessin through Logres: Prelude"
andrewmcdonagh - 08 Mar 2007 19:04 GMT > Daniel Pitts wrote : > [quoted text clipped - 58 lines] > -- > Test Sig By using the enum in this way and having code spread throughout your app which checks which enum is being referenced to decide what to do, it sounds like your design needs to be 'normalised' in an OO way...as Daniel suggests, by moving the decision making logic to a polymorphic call on a Normal class. I say normal class, because I think going from what you describe, you don't need an enum - you need a normal set of classes.
Don't worry that you want to do different things with different parameters, for each 'enum', this is solvable in a polymorphic way too. (its the Strategy Pattern as Daniel says).
Essentially, keep the phrase 'Tell, don't ask' in mind when designing your app, and this OO approach will naturally fall out.
Tell your objects to decide what they want to do, dont ask them for information and then make the decision....
Andrew
Wojtek - 08 Mar 2007 21:56 GMT andrewmcdonagh wrote :
> By using the enum in this way and having code spread throughout your > app which checks which enum is being referenced to decide what to do, > it sounds like your design needs to be 'normalised' in an OO way I can see that, except that I have delibrately de-normalized between use cases. The use cases are pretty well separated from each other.
I can delete a use case without affecting any other use case. Except of course that that functionality has gone, which may mean that the user can no longer maintain helper tables. But it will run OK.
Everything else is in a framework. Those classes cannot be removed as they are used everywhere.
So if an enum is in the framework, then it should NOT know about any business logic which uses it.
For instance (putting on fire-proof-coat) :-) ---------------- public class Value { // these are column IDs which we store in the database // - do NOT change them, only add new ones // - they must be unique private static int LOGIC_AND_ID = 0; private static int LOGIC_OR_ID = 1;
// these are column IDs which we store in the database // - do NOT change them, only add new ones
public static enum Logic { /** * All values must return true */ AND(LOGIC_AND_ID),
/** * Any value can be true */ OR(LOGIC_OR_ID);
private Logic( int databaseID ) { ivDatabaseID = databaseID; }
private int ivDatabaseID;
public int getDatabaseID() { return ivDatabaseID; }
/** * Returns the Logic for a given database id number<br><br> * Used to find a Logic from a Web page field or database column */ public static Logic getLogic( int databaseID ) throws EnumNotFoundException { for (Logic logic : Logic.values()) if (logic.getDatabaseID() == databaseID) return logic;
throw new EnumNotFoundException( Logic.class.getName() + "(id: " + databaseID + ")" ); } }
// .. other universal values, such a number of milli-seconds in a // second, minute, hour, day, and so forth } ----------------
Mind you this is untested code. But it should work (famous last words). At least the compiler does not complain :-)
 Signature Wojtek :-)
Wojtek - 09 Mar 2007 01:13 GMT Wojtek wrote :
> private static int LOGIC_AND_ID = 0; > private static int LOGIC_OR_ID = 1; These should be: private static final int LOGIC_AND_ID = 0; private static final int LOGIC_OR_ID = 1;
Late in the day....
 Signature Wojtek :-)
Wojtek - 07 Mar 2007 19:06 GMT Daniel Dyer wrote :
> The > compiler knows what class the enum constants are from because it knows the > type of the argument to the switch statement. Argh, inconsistencies drive me nuts...
So the compiler knows about the enum constants from the switch, but If I have:
if ( thing == ONE )
That fails because I have not qualified the enum constant.
 Signature Wojtek :-)
John W. Kennedy - 08 Mar 2007 02:55 GMT > Daniel Dyer wrote : >> [quoted text clipped - 9 lines] > > That fails because I have not qualified the enum constant. In every ""switch(Enum), each "case" /must/ be a value of Enum. An "if", on the other hand, can be any expression that eventually boils down to a boolean.
 Signature John W. Kennedy "The blind rulers of Logres Nourished the land on a fallacy of rational virtue." -- Charles Williams. "Taliessin through Logres: Prelude"
Patricia Shanahan - 08 Mar 2007 02:58 GMT >> Daniel Dyer wrote : >>> [quoted text clipped - 13 lines] > on the other hand, can be any expression that eventually boils down to a > boolean. That still does not explain why qualification is prohibited. It would appear to be harmless, and in some situations make code more orthogonal, provided the types match.
Patricia
Daniel Pitts - 08 Mar 2007 04:56 GMT > >> Daniel Dyer wrote : > [quoted text clipped - 19 lines] > > Patricia I agree, but what about this case (no pun intended):
public void foo(Thing thing) { Integer ONE = 1; switch (thing) { case ONE: // case branches are never expression. // so no ambiguity } if (thing == ONE) { // Ambiguous. } }
I think it has more to do with the fact that there can't ever be ambiguity for case branches.
Tor Iver Wilhelmsen - 08 Mar 2007 22:58 GMT På Thu, 08 Mar 2007 03:58:32 +0100, skrev Patricia Shanahan <pats@acm.org>:
> That still does not explain why qualification is prohibited. It would > appear to be harmless, and in some situations make code more orthogonal, > provided the types match. The compiler's rule enforces that the types match. :)
Using enums in switch statements is exactly equivalent to using their literals in a "normal" switch statement. And the range of an enum is its values: It's pointless to even add a hint of ambiguity by allowing other constant expressions than the enum's synthesized instances.
Patricia Shanahan - 08 Mar 2007 23:21 GMT > På Thu, 08 Mar 2007 03:58:32 +0100, skrev Patricia Shanahan <pats@acm.org>: > [quoted text clipped - 8 lines] > values: It's pointless to even add a hint of ambiguity by allowing other > constant expressions than the enum's synthesized instances. Agreed. The only question I'm asking is why the language prohibits correct qualification of the instance name. Why can't one use "Things.ONE", where "Things" is the type of the enum expression in the switch, and "ONE" is one of its value identifiers?
This is not about what expressions can be used in the case, just about what syntax is permitted in naming them.
I can imagine situations in which human readability would be improved by allowing qualification.
For example, suppose two enums, designed separately, use the same instance identifier with different meanings. The compiler will always remember the type of the switch expression, and pick the right "ONE" based on that. A human might see "ONE" and think "Widget.ONE" even if it should be "Things.ONE", especially if the rest of the uses of "ONE" in the class are "Widget.ONE".
Patricia
Wojtek - 09 Mar 2007 01:20 GMT Patricia Shanahan wrote :
>> På Thu, 08 Mar 2007 03:58:32 +0100, skrev Patricia Shanahan <pats@acm.org>: >> [quoted text clipped - 19 lines] > I can imagine situations in which human readability would be improved by > allowing qualification. And further to this, I can use: java.util.HashMap<String,String> x; or HashMap<String,String> x;
Both are acceptable (barring imports). The switch statement is the first place I have found where you CANNOT use a qualified name.
 Signature Wojtek :-)
Chris Uppal - 09 Mar 2007 01:48 GMT > This is not about what expressions can be used in the case, just about > what syntax is permitted in naming them. > > I can imagine situations in which human readability would be improved by > allowing qualification. It's even worse (IMO) if there's another definition of of the enumeration element's name which would normally be expected to hide the other identifier. I find the attached code particularly unpleasant. See how in one case the compiler /requires/ qualification, in another it /forbids/ it.
-- chris
======================= enum Number { ONE, TWO }
public class Test { private static final int ONE = 1, TWO = 2;
public static void main(String[] args) { Number n = Number.ONE; /* Number nn = ONE; -- error: incompatible types */
int m = ONE; int mm = Test.ONE;
switch (n) { case ONE: System.out.println("n is Number.ONE"); break; case TWO: System.out.println("n is Number.TWO"); break; }
/* not legal at all.... switch (n) { case Number.ONE: System.out.println("n is Number.ONE"); break; case Number.TWO: System.out.println("n is Number.TWO"); break; } */
switch (m) { case ONE: System.out.println("m is Number.ONE"); break; case TWO: System.out.println("m is Number.TWO"); break; }
switch (m) { case Test.ONE: System.out.println("m is Number.ONE"); break; case Test.TWO: System.out.println("m is Number.TWO"); break; }
} } =======================
Andreas Leitgeb - 09 Mar 2007 10:02 GMT > It's even worse (IMO) if there's another definition of of the enumeration > element's name which would normally be expected to hide the other identifier. > I find the attached code particularly unpleasant. It's not all that bad: Wherever you could actually use an *any*-qualified item, you can add a qualification.
Wherever only one qualification is possible, it's implicit and you don't specify it.
> enum Number { ONE, TWO } add this: class Foo { static final Number ONE=Number.TWO; static final int TWO=1; }
> public class Test { > private static final int ONE = 1, TWO = 2; > [... inside main() ...] > Number n = Number.ONE; > /* Number nn = ONE; -- error: incompatible types */ of course! since ONE is wrong type, but you could just as legally use Foo.ONE here and you couldn't use that in a switch.
John W. Kennedy - 09 Mar 2007 23:06 GMT > Agreed. The only question I'm asking is why the language prohibits > correct qualification of the instance name. Why can't one use > "Things.ONE", where "Things" is the type of the enum expression in the > switch, and "ONE" is one of its value identifiers? I smell a reaction to the quarrel over whether "myWidget.widgetCount()" should have been allowed as an alias of "Widget.widgetCount()".
 Signature John W. Kennedy "I want everybody to be smart. As smart as they can be. A world of ignorant people is too dangerous to live in." -- Garson Kanin. "Born Yesterday" * TagZilla 0.066 * http://tagzilla.mozdev.org
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|