Naming conventions
The difficulty of naming things
There are only two hard things in Computer Science: cache invalidation and naming things.
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:
getX()
, whereX
is the name of the property (getter)setX(v)
, whereX
is the name of the property andv
its new value (setter)
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:
- it introduces unnecessary complexity in an otherwise simple convention
- it sometimes results in inaccurate or plain wrong phrasing:
isValid
makes sense, butisErrors
is not and should behasErrors
instead
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:
getX(k)
, whereX
is the name of the property andk
its key (getter)setX(k, v)
, whereX
is the name of the property,k
its key andv
its new value (setter)
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:
String Object.toString()
(Java)String.toInt(radix: Int = 10): Int
(Kotlin)
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:
- their name is more expressive than the name of a constructor
- they can return an existing value rather than creating one
- they can return a subclass, possibly even a private one
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");