Chapter 20. Performing I/O to Passing Data

Input and output are problematic in code. Our program is subject to errors talking to the outside world when files disappear or network sockets fail. I/O is also an action and so limits our ability to reason with and refactor our code. How can we limit the scope of the problems that I/O causes?

Now that earlier chapters have built some foundations, we’re going to up the pace here, going straight into refactoring and learning lessons as we go.

Listening to Tests

In Chapter 10, we looked at some Java code that produced a report for marketing. When we left the code, we had introduced extension functions to the HighValue​Custo⁠mersReport, giving us:

@Throws(IOException::class)
fun generate(reader: Reader, writer: Writer) {
    val valuableCustomers = reader
        .readLines()
        .toValuableCustomers()
        .sortedBy(CustomerData::score)
    writer.appendLine("ID\tName\tSpend")
    for (customerData in valuableCustomers) {
        writer.appendLine(customerData.outputLine)
    }
    writer.append(valuableCustomers.summarised())
}

private fun List<String>.toValuableCustomers() = withoutHeader()
    .map(String::toCustomerData)
    .filter { it.score >= 10 }

private fun List<String>.withoutHeader() = drop(1)

private fun List<CustomerData>.summarised(): String =
    sumByDouble { it.spend }.let { total ->
        "\tTOTAL\t${total.toMoneyString()}"
    }

Here are the tests after conversion to Kotlin:

class ...

Get Java to Kotlin now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.