Don't use String for method options, use an enum!
Posted by Simon Larsén in Programming
In this article, we are going to have a look at a method that accepts an option.
That is to say, it accepts an argument that somehow decides how it operates. If
you use a lot of libraries in your day-to-day programming, you're bound to come
across methods that accept String
values as such options, and you've probably
been infuriated by you misspelling the options, or just trying to figure out
what options there are in the first place. That suggests that there must be a
better solution, and as you may have figured out by now,
enums is that
solution.
Note: This article discusses enums in Java, but the very same arguments are valid for any language that has support for enum types, or something comparable.
The problem: What options do I have?
Consider the following method that formats a String
according to an option
supplied as another String
:
Yes, this is a somewhat contrived example, but bear with me!
public static String format(String str, String option) {
switch (option) {
case "upper":
return str.toUpperCase();
case "lower":
return str.toLowerCase();
default:
throw new IllegalStateException("Internal errror, unmatched option " + option);
}
}
We could then use the method something like this:
format("hello", "upper");
Can you see any problems with this? Well first of all, if you don't have access
to the source, how do you know which options can be passed? There are an
infinite amount of strings, after all. At best, the Javadoc will say precisely
which values are valid options, but that is not always the case even in the
Java standard
library.
But even if all of the options are clearly documented at one point, it would be
so easy for a developer to add or remove an option, and forget to enact the
corresponding change in the Javadoc. It is also difficult to have automatic
checks that actually verify that all possible options are documented. And even
assuming that all options are properly documented at all times, the compiler
can't distinguish which String
values are valid and which are not, so a user
misspelling an option won't know until runtime.
The solution: enums!
My goal here is not to explain the ins and outs of what enums are, but rather show a use case. In short, an enum is a data type with a (typically very) limited amount of possible values (you can read more about it here). Now, let's instead define this enum type:
public enum FormatOption {
UPPER,
LOWER;
}
And refactor the method with it:
public static String format(String str, FormatOption option) {
switch (option) {
case UPPER:
return str.toUpperCase();
case LOWER:
return str.toLowerCase();
default:
throw new IllegalStateException("Internal errror, unmatched option " + option);
}
}
This method can then be used like:
format("hello", FormatOption.UPPER);
With this small alteration, we have eliminated all of the problems mentioned
before. The possible values for the option
argument are now self-documented
in the FormatOption
enum. Additionally, any modern IDE will kindly list the
possible values when you type FormatOption.
, such that a programmer does not
even necessarily need to consult the documentation, assuming that the enum
values have descriptive enough names. The compiler can also distinguish between
FormatOption.UPPER
and a misspelling such as FormatOption.UPER
, as the
latter is not defined, so runtime errors due to invalid options is no longer a
problem.
What's the catch?
So what's the catch? Well, if you have many methods like this, you'll end up with a lot of enum types. Personally, I think that's totally worth it, and you could also nest the enums inside the classes that use them to reduce their overall footprint in the project. The whole thing could then look like this:
public class Formatter {
public static enum Option {
UPPER,
LOWER;
}
public static String format(String str, Option option) {
String result;
switch (option) {
case UPPER:
return str.toUpperCase();
case LOWER:
return str.toLowerCase();
default:
throw new IllegalStateException("Internal errror, unmatched option " + option);
}
}
}
Didn't add that much complexity now, did it?
Summary
If you have a method that you want to pass options to, use enums.
That's really all there is to it. Enums are quite widely used in the Java
standard library as well, such as
StandardCopyOption,
StandardOpenOption and
LinkOption
in the java.nio.file
API,
which are used much in the same way as I used the enum in this article.
Hopefully, having read this article, you won't be creating any more APIs that
accept String
options!