Naming conventions

Java Kotlin

The difficulty of naming things

There are only two hard things in Computer Science: cache invalidation and naming things.

Phil Karlton

Naming things is hard indeed, and is even harder when one tries to come up with sensible naming conventions. Conventions are important though, as they make names themselves conveyors of meaning, thus reducing the need to overdocument the objects they refer to.

In this article, I attempt to formalize the naming conventions I use in my Java and Kotlin code.

Naming conventions

Property accessors

Property accessors are defined by the JavaBeans Spec and are prevalent in the Java ecosystem.

Naming:

According to the spec, getters for boolean properties should follow a different convention: isX(), where X is the name of the property. I don’t like this exception, for several reasons:

Fortunately, most tools that rely on accessors support the getX() convention for boolean properties, too.

Keyed/indexed properties require the accessors to have an additional parameter for the key/index. This parameter must be the first.

Naming:

Conversion functions

Conversion functions return a copy of a value as a different type. Conversion methods act as conversion functions for the object itself.

Naming: toX(), where X is the type of the result

If the conversion can be parameterized, conversion functions can have parameters. It is helpful to provide sensible default values for these parameters (either by overloading the method or by defining default parameter values).

If the conversion can fail, conversion functions may throw a runtime exception. The exact exception depends on the form of the conversion function: conversion functions that take the value as parameter typically throw an IllegalArgumentException, while conversion methods should throw an IllegalStateException instead.

Examples:

View functions

View functions return a value as a different type. View methods act as view functions for the object itself.

View functions differ from conversion functions in that their result is the value itself, not a copy. This distinction is important for mutable values, as any modifications made to the result of a view function are made to the original value itself.

Naming: asX(), where X is the type of the result

View functions should rarely be parameterizable and should rarely fail.

Factory functions

Factory functions create values. They have several advantages over constructors:

Naming: createX(), where X is the name of the value

Factory functions can have parameters.

The word create suggests a new instance is created, which may not be the case (e.g. cached instances). I consider this an implementation detail that should not leak in the name.

When factory functions are static methods, it may be tempting to assume their invocations will be prefixed with the name of the class, and to settle for the name createInstance:

class Player {
    static Player createInstance(String name) {
        // ...
    }
}
import Player;

// It's clear we want a player, but a little verbose
Player player = Player.createInstance("Laurent");

However, I generally prefer to use static imports and therefore make the name more explicit:

class Player {
    static Player createPlayer(String name) {
        // ...
    }
}
import Player;
import static Player.createPlayer;

// It's still clear we want a player, and less verbose
Player player = createPlayer("Laurent");