Writing (Slightly) Cleaner Code With Collections and Optionals
See how utility methods provided by Kilo's Collections and Optionals classes can help you write more readable and maintainable Java code.
Join the DZone community and get the full member experience.
Join For FreeKilo is an open-source project for creating and consuming RESTful and REST-like web services in Java. Among other things, it includes the Collections and Optionals classes, which are designed to help simplify code that depends on collection types and optional values, respectively. Both are discussed in more detail below.
Collections
Kilo’s Collections class provides a set of static utility methods for declaratively instantiating list, map, and set values:
public static <E> List<E> listOf(E... elements) { ... }
public static <K, V> Map<K, V> mapOf(Map.Entry<K, V>... entries) { ... }
public static <E> Set<E> setOf(E... elements) { ... }
They offer an alternative to similar methods defined by the List, Map, and Set interfaces, which return immutable instances and do not permit null values. The following immutable variants are provided as well:
public static <E> List<E> immutableListOf(E... elements) { ... }
public static <K, V> Map<K, V> immutableMapOf(Map.Entry<K, V>... entries) { ... }
public static <E> Set<E> immutableSetOf(E... elements) { ... }
Map entries can be declared using the following method:
public static <K, V> Map.Entry<K, V> entry(K key, V value) { ... }
Collectively, these methods represent a Java approximation of the “collection literal” syntax supported by languages like JavaScript and Swift. For example (in Swift, lists are called “arrays” and maps are called “dictionaries”):
var array = [
"one",
"two",
"three"
]
let immutableArray = [
"four",
"five",
"six"
]
var dictionary = [
"a": 1,
"b": 2,
"c": 3
]
let immutableDictionary = [
"d": 4,
"e": 5,
"f": 6
]
Using Collections, the code might look like this (the methods have been statically imported to reduce verbosity):
var list = listOf(
"one",
"two",
"three"
);
var immutableList = immutableListOf(
"four",
"five",
"six"
);
var map = mapOf(
entry("a", 1),
entry("b", 2),
entry("c", 3)
);
var immutableMap = immutableMapOf(
entry("d", 4),
entry("e", 5),
entry("f", 6)
);
It’s not quite as concise as the Swift version, but it is structurally and visually similar, making it easy to read, understand, and modify when needed. It also looks a lot like Kotlin, though the mutability options are reversed:
val list = mutableListOf(
"one",
"two",
"three"
)
val immutableList = listOf(
"four",
"five",
"six"
)
val map = mutableMapOf(
"a" to 1,
"b" to 2,
"c" to 3
)
val immutableMap = mapOf(
"d" to 4,
"e" to 5,
"f" to 6
)
Optionals
The Optionals class contains methods for working with optional (or “nullable”) values:
public static <T> T coalesce(T value, Supplier<? extends T> supplier) { ... }
public static <T, U> U map(T value, Function<? super T, ? extends U> transform) { ... }
These are provided as a less verbose alternative to similar methods defined by the java.util.Optional class. They also represent an attempt to emulate some of the null safety features of Kotlin and Swift.
For example, the following Java code assigns a default value to a variable if the original value is null:
Object a = null;
var b = a;
if (b == null) {
b = 123;
}
Using Java’s Optional class, the code could be simplified as follows, reducing complexity and eliminating the variable reassignment:
Object a = null;
var b = Optional.ofNullable(a).orElse(123);
However, this is still somewhat awkward and verbose. In Kotlin, the code could be written like this, using the null-coalescing Elvis operator:
val a = null
val b = a ?: 123
Using the coalesce() method of Kilo’s Optionals class, it could be written as shown below:
Object a = null;
var b = coalesce(a, () -> 123);
This is almost as simple as the Kotlin version. If a is not null, it is returned immediately. Otherwise, the provided supplier is invoked to produce the default value of 123.
Null-Safe Calls
Kotlin also provides a safe call operator designed to help avoid NullPointerExceptions. For example, the following Java code would throw when attempting to invoke the length()method on a:
String a = null;
var b = a.length();
The code could be rewritten as follows to avoid the exception, at the cost of increased complexity and decreased readability:
String a = null;
int b;
if (a != null) {
b = a.length();
} else {
b = 0;
}
In Kotlin, it can be reduced to the following:
val a: String? = null
val b = a?.length ?: 0
When a is not null, the length property is accessed, and the return value is assigned to b. However, when a is null, length is not read, and the safe call operator returns null. The Elvis operator ensures that a non-null default value of 0 is assigned to b in this case.
Using Java’s Optional class, the code could be written like this:
String a = null;
var b = Optional.ofNullable(a).map(String::length).orElse(0);
This is better than the original version, but it is still more awkward than necessary. Using Optionals, it could be written in a way that more closely resembles the Kotlin version:
String a = null;
var b = coalesce(map(a, String::length), () -> 0);
Conclusion
Will Collections and Optionals drastically alter the way you write Java code? Probably not. But they can help make your code more modern, readable, and maintainable.
If you’re currently using Kilo, you already have access to these features. If not, you can easily adopt them by adding org.httprpc:kilo-client to your project dependencies.
For more information, see the project documentation.
Published at DZone with permission of Greg Brown. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments