Type-safe strings in Java

Type-safe strings in Java

Often in our code we want to base program logic on the value of particular strings, but strings get misspelled, and appear in many places in the code, and after a while we forget where they all are and why they are what they are, and we have ever more strings, and it's a mess to keep them all disentangled and spelled right, and bugs slip in.

Strings are dangerous. Consider an innocent-seeming 'switch' that uses Java's slick new switch-on-a-string capability. Let's make a decision based on user input. For example, take the five basic tastes:

{sweet, sour, salty, bitter, savory}

Let's branch our program path based on which taste the operator chooses. 

We might go for simply reading the user choice into a 'String' variable called 'taste':

switch (taste) {

case "sweet":
  emit("Yum!");
  break;

case "sour":
  emit("Ooh!");
  break;

case "salty":
  emit("Woh!");
  break;

case "bitter":
  emit("Eww!");
  break;

case "savory":
  emit("Tch!");
  break;

}

Problems arise when many places in the code need these values. We might have to print out a `taste` as a report header one place, count all the occurrences of one in another, compare a value in an object to a given `taste`, or a host of other things. Hold the phone, someone just discovered a new taste sensation, call it "caliente". We need to add that to the model, meanwhile dozens maybe hundreds of modules are using these constants. How do we detect a misspelling? You could have a `case "biter":` somewhere and you'd never detect it because it simply is never executed.

We need a type-safe `String`!

So use an `enum`. An enum can encapsulate required strings in an immutable, type-safe way that guarantees no misspellings, and has full compiler support.

First, define your enum constants to have "friendly" representations:

public enum Taste {

 SWEET("Sweet"),
 SOUR("Sour"),
 SALTY("Salty"),
 BITTER("Bitter"),
 SAVORY("Savory"),
 ;

 private final String representation;

 Taste(String rep) { this.representation = rep; }

 @Override public String toString() { return representation; }

 public static Taste fromString(String rep) {
   if (rep == null || rep.isEmpty()) {
     return null;
   }
   for ( Taste taste : values() ) {
     if (rep.equals(taste.toString())) {
       return taste;
     }
   }
   return null;
 }
}

Now you can safely switch on the enum `Taste`, and the compiler will not let you spell a `Taste` value `BITER`. You have one singular location to change the string representation, say if the original author defined `BITTER("biter")`. Code analysis tools will detect `switch` statements that leave out a value.

You can use the enum almost like a string because `toString()` is automatically invoked in `String` concatenation:

System.out.println("The taste is " + taste);

All the benefits of a `String`, plus type safety, plus extra compiler support. You can use enums as type-safe strings.

(Pictures and text copyright © 2015-2017 Lewis S. Bloch. All rights reserved.)

To view or add a comment, sign in

More articles by Lew Bloch

  • They won't have thought of this!

    Wesley Snipes served almost two and a half years in jail for Federal tax crimes, and owes tens of millions in back…

    5 Comments
  • The magic key to software QA

    My software quality-assurance (QA) strategy now can realize the promise of software QA. This is a bold claim, but…

    7 Comments

Others also viewed

Explore content categories