Effective Kotlin: Item 29 — Favor generic types

As with Java, there is no excuse in forcing clients to cast types when using your code especially when making a class generic is often trivial. Item 29 of Joshua Bloch’s brilliant Effective Java covers this by showing an example of generifying a Stack class which might look as follows in Kotlin:

class Stack(initialCapacity: Int = 16) {
private var elements = arrayOfNulls<Any?>(initialCapacity)
private var size = 0

val isEmpty = size == 0

fun push(e: Any) {
ensureCapacity()
elements[size++] = e
}

fun pop(): Any {
if (size == 0) throw EmptyStackException()
return (elements[--size] as Any).also {
elements
[size] = null // Eliminate obsolete reference
}
}

private fun ensureCapacity() {
if (elements.size == size)
elements = Arrays.copyOf(elements, 2 * size + 1)
}
}
val stack = Stack()
stack.push(5)
val value = stack.pop() as Int

The generic version might look as follows where as you cannot use a non reified type in Array we perform an unchecked cast :

class Stack<T : Any>(initialCapacity: Int = 16) {
@Suppress("UNCHECKED_CAST")
private var elements = arrayOfNulls<Any?>(initialCapacity) as Array<T?>
private var size = 0

val isEmpty = size == 0

fun push(e: T) {
ensureCapacity()
elements[size++] = e
}

fun pop(): T {
if (size == 0) throw EmptyStackException()
return (elements[--size] as T).also {
elements
[size] = null // Eliminate obsolete reference
}
}

private fun ensureCapacity() {
if (elements.size == size)
elements = Arrays.copyOf(elements, 2 * size + 1)
}
}

One thing you may spot with the code above is I’ve explicitly made the generic type parameter T extend Any as the default upper bound when none is specified is Any?.

As Java’s generics use raw types the same client code will work in Java without modification, albeit with a warning on push:

Stack stack = new Stack();
stack.push(5); // WARNING: Unchecked call to 'push(T)'
int value = (Integer) stack.pop();

However, in Kotlin all calls to generic classes must have a type defined as star-projections replace raw types. The impact is that on the Kotlin side generifying a class is a breaking change whether converting a Java or Kotlin class. You should still favour generic types, but ensure you document any changes to existing classes for your users.

Each week I am looking at “items” from Joshua Bloch’s well-respected book, Effective Java to see how it applies to Kotlin. You can find the rest of the items I’ve covered at Effective Kotlin. Please let me know your thoughts.

Matt Dolan has been eating doughnuts and developing with Android since the dark days of v1.6.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store