Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Coping With Stringly-Typed Code

DZone's Guide to

Coping With Stringly-Typed Code

When a majority of your fields and parameters are Strings, your code can be considered Stringly-typed. See what problems it causes and how to solve them.

· Java Zone
Free Resource

Try Okta to add social login, MFA, and OpenID Connect support to your Java app in minutes. Create a free developer account today and never build auth again.

Most developers have strong opinions regarding whether a language should be strongly-typed or weakly-typed, whatever notions they put behind those terms. Some also actively practice stringly-typed programming — mostly without even being aware of it. It happens when most of attributes and parameters of a codebase are String. In this post, I will make use of the following simple snippet as an example:

public class Person {
    private final String title;
    private final String givenName;
    private final String familyName;
    private final String email;

    public Person(String title, String givenName, String familyName, String email) {
        this.title = title;
        this.givenName = givenName;
        this.familyName = familyName;
        this.email = email;
    }
    ...
}


The Original Sin

The problem with that code is that it’s hard to remember which parameter represents what and in which order they should be passed to the constructor.

Person person = new Person("john@doe.me", "John", "Doe", "Sir");


In the previous call, the email and the title parameter values were switched. Ooops.

This is even worse if more than one constructor is available, offering optional parameters:

public Person(String givenName, String familyName, String email) {
    this(null, givenName, familyName, email);
}

Person another = new Person("Sir", "John", "Doe");


In that case, title was the optional parameter, not email. My bad.

Solving the Problem the OOP Way

Object-oriented programming and its advocates have a strong aversion to stringly-typed code for good reasons. Since everything in the world has a specific type, so must it be in the system.

Let’s rewrite the previous code à la OOP:

public class Title {
    private final String value;
    public Title(String value) {
        this.value = value;
    }
}

public class GivenName {
    private final String value;
    public FirstName(String value) {
        this.value = value;
    }
}

public class FamilyName {
    private final String value;
    public LastName(String value) {
        this.value = value;
    }
}

public class Email {
    private final String value;
    public Email(String value) {
        this.value = value;
    }
}

public class Person {
    private final Title title;
    private final GivenName givenName;
    private final FamilyName familyName;
    private final Email email;

    public Person(Title title, GivenName givenName, FamilyName familyName, Email email) {
        this.title = title;
        this.givenName = givenName;
        this.familyName = familyName;
        this.email = email;
    }
    ...
}

Person person = new Person(new Title(null), new FirstName("John"), new LastName("Doe"), new Email("john@doe.me"));


That way drastically limits the possibility of mistakes. The drawback is a large increase in verbosity — which might lead to other bugs.

Languages to the Rescue

Verbosity is, unfortunately, the mark of Java. Some other languages (Kotlin, Scala, etc.) would be much more friendly to this approach, not only for class declarations, but also for object creation.

Let’s port class declarations to Kotlin:

class Title(val value: String?)
class GivenName(val value: String)
class FamilyName(val value: String)
class Email(val value: String)

class Person(val title: Title, val givenName: GivenName, val familyName: FamilyName, val email: Email)


This is much better, thanks to Kotlin! And now object creation:

val person = Person(Title(null), GivenName("John"), FamilyName("Doe"), Email("john@doe.me"))


For this, verbosity is only marginally decreased compared to Java.

Named Parameters to the Rescue

OOP fanatics may stop reading there, for their way is not the only one to cope with stringly-typed.

One alternative is about named parameters, and is incidentally also found in Kotlin. Let’s get back to the original stringly-typed code, port it to Kotlin and use named parameters:

class Person(val title: String?, val givenName: String, val familyName: String, val email: String)

val person = Person(title = null, givenName = "John", familyName = "Doe", email = "john@doe.me")

val another = Person(email = "john@doe.me", title = "Sir", givenName = "John", familyName = "Doe")


A benefit of named parameters besides coping with stringly-typed code is that they are order-agnostic when invoking the constructor. Plus, they also play nice with default values:

class Person(val title: String? = null, val givenName: String, val familyName: String, val email: String? = null)

val person = Person(givenName = "John", familyName = "Doe")
val another = Person(title = "Sir", givenName = "John", familyName = "Doe")


Type Aliases to the Rescue

While looking at Kotlin, let’s describe a feature released with 1.1 that might help.

type alias is as its name implies a name for an existing type; the type can be a simple type, a collection, a lambda — whatever exists within the type system.

Let’s create some type aliases in the stringly-typed world:

typealias Title = String
typealias GivenName = String
typealias FamilyName = String
typealias Email = String

class Person(val title: Title, val givenName: GivenName, val familyName: FamilyName, val email: Email)

val person = Person(null, "John", "Doe", "john@doe.me")


The declaration seems more typed. Unfortunately, object creation doesn’t bring any betterment.

Note the main problem of type aliases is that they are just that — aliases: no new type is created so if two aliases point to the same type, all three are interchangeable with one another.

Libraries to the Rescue

For the rest of this post, let’s go back to the Java language.

Twisting the logic a bit, parameters can be validated at runtime instead of compile-time with the help of specific libraries. In particular, the Bean validation library does the job:

public Person(@Title String title, @GivenName String givenName, @FamilyName String familyName, @Email String email) {
    this.title = title;
    this.givenName = givenName;
    this.familyName = familyName;
    this.email = email;
}


Admittedly, it’s not the best solution… but it works.

Tooling to the Rescue

I have already written about tooling and that it’s as important (if not more) as the language itself.

Tools fill gaps in languages, while being non-intrusive. The downside is that everyone has to use it (or find a tool with the same feature).

For example, when I started my career, coding guidelines mandated developers to order methods by alphabetical order in the class file. Nowadays, that would be senseless, as every IDE worth its salt can display the methods of a class in order.

Likewise, named parameters can be a feature of the IDE, for languages that lack it. In particular, latest versions of IntelliJ IDEA emulates named parameters for the Java language for types that are deemed too generic. The following shows the Person class inside the IDE:

Image title

Conclusion

While proper OOP design is the historical way to cope with stringly-typed code, it also makes it quite verbose and unwieldy in Java. This post describes alternatives, with their specific pros and cons. Each needs to be evaluated in the context of one’s own specific context to decide which one is the best fit.

Build and launch faster with Okta’s user management API. Register today for the free forever developer edition!

Topics:
java strings ,kotlin ,type aliasing ,named parameters ,java

Published at DZone with permission of Nicolas Frankel, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}