Specialized Classes
After we work with a language for a while, we start to develop patterns and shortcuts. This is certainly true of object-oriented languages. We examine a few of the specialized classes that have emerged over the several decades we've been programming with them.
Data Classes
Putting behaviors and their state together into coherent classes is a noble pursuit. Early Java took object orientation to the extreme and developed a reputation for being verbose. Many classes are mere data classes, just bundles of data. Writing their getters, setters, and constructors for data classes is monotonous. Languages therefore started offering an abbreviated syntax for defining them. Java 16, which was released in 2021, introduced records, which compress a class definition down to a single line:
public record Tshirt(Color color, Size size) {}
public record Tshirt(Color color, Size size) {}The Java compiler turns a record definition into a regular class with a constructor, getters, setters, toString, and private instance variables. It additionally defines methods hashCode and equals so instances can be reliably stored and retrieved from hashed collections.
Meanwhile, Ruby has supported an abbreviated means of defining data classes since 1995. It requires no special syntax. We simply make an instance of Struct, passing a list of instance variable names as symbols:
Tshirt = Struct.new(:color, :size)
medium_pink = Tshirt.new(:pink, :medium)
Tshirt = Struct.new(:color, :size) medium_pink = Tshirt.new(:pink, :medium)
Calling Struct.new returns a class, which is a radical notion. From this class, we make instances.
Singleton
Classes permit many instances, but some objects are one of a kind. An object of which there is only a single instance is called a singleton. For example, suppose we want to issue logging messages from anywhere in a project, and we want all the messages to funnel into a single output stream. We want a singleton Logger object that any function may access, but we don't want any other Logger objects floating around and sowing confusion.
We create our own singleton using a class with a static variable initialized to hold the single instance. By making the constructor private, no other entity may create a second instance. This Java class defines a singleton Logger:
public class Logger {
public static final SINGLETON = new Logger();
private Logger() {/* ... */}
public void warn(String message) {/* ... */}
public void err(String message) {/* ... */}
}
public class Logger {
public static final SINGLETON = new Logger();
private Logger() {/* ... */}
public void warn(String message) {/* ... */}
public void err(String message) {/* ... */}
}The only way to log a message is by going through the singleton:
Logger.SINGLETON.warn("network lost");
Logger.SINGLETON.warn("network lost");Kotlin provides syntactic support for creating singletons in a simpler manner. A singleton is defined just like a class, only the keyword object is used instead of class:
object Logger {
fun warn(message: String) {
println("Warning: $message")
}
fun err(message: String) {
println("Error: $message")
}
}
object Logger {
fun warn(message: String) {
println("Warning: $message")
}
fun err(message: String) {
println("Error: $message")
}
}There's no static variable to act as the receiver. Instead, the singleton's methods are invoked as if they were static:
fun main() {
Logger.warn("network lost")
}
fun main() {
Logger.warn("network lost")
}No constructor is ever explicitly called. The singleton is implicitly instantiated upon its first access.
Utility
A class with only static methods is a utility class. There's absolutely no reason to make instances of a utility class, so we make its do-nothing constructor private. Having to turn off a language's default features is awkward and is a sign that the language needs to be altered to better fit how it's being used. A problem with many object-oriented languages is that classes serve two roles: they organize code and they model a component of a system. These roles should be independent, but they are not. In a utility class, we want to organize code but not make an instantiable model.