--- title: The Humble Enumeration --- # What in the heck is an enum? ```java {0|1|1-2|1-3|1-5} enum MentalState { WAIT, OH_GOD, WHAT } ``` ## And _why?_ ```java {0|1|1-2|1-3|1-5} enum UsefulFor { EVERYTHING, A_FEW_THINGS, NOTHING_AT_ALL } ``` # What in the heck is an enum? ```java enum MentalState { WAIT, OH_GOD, WHAT } ``` ## And _why?_ ```java enum UsefulFor { EVERYTHING, // ??? A_FEW_THINGS, // ??? NOTHING_AT_ALL // ??? } ``` # Enumerated types ## An enum is essentially a known set of distinct values. ### A couple of common enums would be things like: * Playing card suits (HEART, SPADE, CLUB, DIAMOND) * Days of the week (SUNDAY, MONDAY, etc.) * Log levels (DEBUG, INFO, WARNING, ERROR, FATAL) * Etc. # Okay, that's great, but how do I know when to use one? ## (Or, "Booleans? More like, _Fooleans!_") So, picture this. You've got some code that analyzes a computer network. There is a lovely Computer _class_: ```java public class Computer { String hostname; double maxPowerConsumption; boolean on; } ``` And a lovely _function_ that calculates how much power a collection of computers might use: ```java public double estimateWattage(List computers) { double sum = 0; for (var computer : computers) { if (computer.on) { sum += computer.maxPowerConsumption; } } return sum; } ``` # This works great. ## Until someone says... "What about computers in standby?" # Oh. # True. # Well, okay, maybe let's add a new _inStandby_ field to Computer? ```java {5} public class Computer { String hostname; double maxPowerConsumption; boolean on; boolean inStandby; } ``` ## and update our wattage function: ```java {7-8} public double estimateWattage(List computers) { double sum = 0; for (var computer : computers) { if (computer.on) { sum += computer.maxPowerConsumption; } else if (computer.inStandby) { sum += computer.maxPowerConsumption * 0.01; } } return sum; } ``` That works. Probably. But it's starting to look a little ugly. And, wait. Is a computer in standby... powered on? Or is it off? Hmm... # No matter how you answer the question, you've got yourself a problem. ## There is now an _invalid_ state that an instance of Computer could end up in. # Let's say we decide that a computer inStandby is _on._ ## That means that having an instance where `.inStandby == true` and `.on == false` is _illegal._ And every part of your code better understand that, because the compiler does ___not.___ ### The technical term for this is, #### "Yucky" ### or ##### "Ew" Hmmmmmm.... # Well. We could try a String. ```java {0|4} public class Computer { String hostname; double maxPowerConsumption; String powerState; } ``` ## But then, how do we know if Computer's powerState is valid? ### Are we going to _enjoy_ writing code like this? ```java public static final String IN_STANDBY = "IN_STANDBY"; if (computer.powerState.toUpperCase().equals(IN_STANDBY)) { sum += computer.maxPowerConsumption * 0.01; } ``` #### My guess is, not so much. # Instead, # What if we try an enum? ```java enum PowerState { POWERED_ON, STANDBY, POWERED_OFF } ``` ## Enums can describe more than two distinct states. ### But they're a lot more strict than a String. #### And we can use _that_ inside our Computer class. ```java {0|4} public class Computer { String hostname; double maxPowerConsumption; PowerState powerState; } ``` # Then estimateWattage() cleans up pretty nicely! ```java {6-14} public double estimateWattage(List computers) { double sum = 0; for (var computer : computers) { switch (computer.powerState) { case POWERED_ON: sum += computer.maxPowerConsumption; break; case STANDBY: sum += computer.maxPowerConsumption * 0.01; break; case POWERED_OFF: // Do nothing break; } } return sum; } ``` # And adding a new power state has become a _lot_ easier: ```java {5-7} switch (computer.powerState) { case POWERED_ON: sum += computer.maxPowerConsumption; break; case IDLE: sum += computer.maxPowerConsumption * 0.12; break; case STANDBY: sum += computer.maxPowerConsumption * 0.01; break; case POWERED_OFF: // Do nothing break; } ``` ## This is pretty much the extent of enum capabilites in C, C++, and C# ### But now _this_ is starting to look repetitive. # So, what if we do some Java? ## Could we store that multiplier on the enum itself? ```java {2-99} enum PowerState { POWERED_ON(1.0), IDLE(0.12), STANDBY(0.01), POWERED_OFF(0); public final double wattageMultiplier; PowerState(double mult) { this.wattageMultiplier = mult; } } ``` # Hey, we can! ## Java lets us associate any arbitrary data with an enum. ### Though it's good practice to stick with something immutable. (Also: note the semicolon after the last value. It was optional before!) # Now, estimateWattage() doesn't need to know what the multipliers are! ```java public double estimateWattage(List computers) { double sum = 0; for (var computer : computers) { var power = computer.powerState; sum += computer.maxPowerConsumption * power.wattageMultiplier; } return sum; } ``` ## It just needs to know that a multiplier _exists._ # And, what the heck, let's use streams. ```java public double estimateWattage(List computers) { return computers.stream() .mapToDouble(computer -> computer.maxPowerConsumption * computer.powerState.wattageMultiplier) .sum(); } ``` ## _Functional!_ ### If you've futzed with streams a lot, you may notice an opportunity here. ## If Computer had some sort of method for: ```java computer.maxPowerConsumption * computer.powerState.wattageMultiplier ``` # We could use that directly in mapToDouble(), via a _method reference_ # So, let's define that method. ```java {2-99} public class Computer { // snip public double currentPowerConsumption() { return maxPowerConsumption * powerState.wattageMultiplier; } } ``` ## Simple enough. # Cool. # And that leaves us with this fairly-neat implementation: ```java public double estimateWattage(List computers) { return computers.stream() .mapToDouble(Computer::currentPowerConsumption) .sum(); } ``` ## It _almost_ doesn't even need a dedicated method anymore. ### But I'll leave that as an exercise for the reader. # Side note ## In Java, you can also define methods directly on an enum. ### For example, say we still wanted a way to check if a computer is on or off. #### We could add a method directly to PowerState: ```java {7-99} enum PowerState { POWERED_ON(1.0), IDLE(0.12), STANDBY(0.01), POWERED_OFF(0); // snip public boolean isOn() { return this != POWERED_OFF; } } ``` #### And that keeps the logic all in one place. # In summary, ## Wait, when should you use enums? # Basically any time you have a _known_ set of _distinct_ states. ## When should you definitely _not_ use an enum? ### Things like: * Names * Numbers * IPs * Prices * Etc. Because they aren't known beforehand, and there are too many to enumerate yourself. ## So, use an enum when you have at least two states. # But probably less than a hundred.