Never, never, never use String in Java – 7 years later

Some years ago I’ve written about never to use String in Java.

“Never, never, never use (unwrapped) String or long or int. Why? Those primitive types have no semantic meaning. They are hard to understand, hard to maintain, and hard to extend.”

Now it is time to revisit this post and see what I’ve learned since then.

The idea is that instead of String for data like firstnames, use a FirstName type instead. This leads to more readable code, as the semantics of data is clearer. Together with more readable APIs this leads to less bugs. For example this Java code

is hard to use, as it doesn’t explain what constraints there are on the data to use the method correctly. Can firstName be empty? One way for a better API is to use Monads like Option in Scala or Maybe in Haskell to make clear which values are optional. In Scala the fact that firstName can be empty (optional) could be expressed as

What other constraints does name and firstName have? To make the API more readable my article those years ago suggested to introduce classes for the parameters. The constructor of these class then makes constraints clear and guards constraints. Our example then becomes

Today a lot of my code uses unboxed tagged types in Scala to enforce constraints on data and make code easier to understand.

One way to write tagged unboxed types is

where Name is the tag. In Scala this is infix notation for a generic type @@ with the type parameters String and Name. The infix notation is the same as @@[String,Name]. This type can easily be declared as

and

With unboxed tagged types in Scala the example code can be rewritten as:

It has the same benefits as the code with those special classes above, e.g. is type safe to use, prevents bugs introduced by using the wrong order of call arguments and ensures constraints on the parameters. The name parameter of type String @@ Name could have the constraints of not being empty, having a minimum number of characters (bad idea when going international), starts with a capital character or does not contain digits or HTML code.

This way not only has less overhead due to the fact that the types are unboxed, but also has the benefit of reusable code, e.g. Count can be used with other base types too. Count could have different constraints for Int or String:

The largest benefit I see, is how the API is easier for me as a developer to use. I see that name is basically a String with some constraints and a tag. In the case of String the developer seeing the code wonders what an name really is. With some conventions he knows there is a Name() method somewhere to check constraints and get a String @@ Name out of a String.

Overall it’s best to combine all approaches like in:

often combined with a FirstName.isValid method to prevent exceptions.

This leads to

and reaps the benefits of a clearer API that is less bug prone to use.