Add a mapping from Interface to Concrete type with Moshi and Kotlin

If you’re using Moshi for your JSON needs, you’ll eventually stumble upon the need to specify the concrete implementation of an interface. For example, you might have a contrived example such as:
interface MammalInterface {
    val name: String
    val age: Int
}

@JsonClass(generateAdapter = true)
data class Human(
    override val name: String, 
    override val age: Int
) : MammalInterface

@JsonClass(generateAdapter = true)
data class MammalList(
    val mammals: List<MammalInterface>
)
So how do we make sure the list of mammals in MammalList uses the concrete Human implementation? With a custom adapter!
/** Map from [TInterface] to [TConcrete] so that when [TInterface] is encountered, it'll be serialised and deserialised using [TConcrete] */
class InterfaceToConcreteAdapter<TInterface, TConcrete : TInterface>(
    private val adapter: JsonAdapter<TConcrete>
) : JsonAdapter<TInterface>() {
    @FromJson
    override fun fromJson(reader: JsonReader): TInterface? = adapter.fromJson(reader)

    @Suppress("UNCHECKED_CAST")
    @ToJson
    override fun toJson(writer: JsonWriter, value: TInterface?) = adapter.toJson(writer, value as TConcrete)
}
The implementation is simple – we just delegate the serialisation to the adapter for the concrete type. We also specify that the concrete type must extend the interface to ensure we don’t get any cast exceptions at runtime. However, since this adapter requires the actual concrete adapter, which Moshi isn’t going to be able to provide itself. For this we’ll need a Factory:

/** Map from [TInterface] to [TConcrete] so that when [TInterface] is encountered, it'll be serialised and deserialised using [TConcrete] */
class InterfaceToConcreteAdapterFactory<TInterface, TConcrete : TInterface>(
    interfaceClass: Class<TInterface>,
    private val concreteClass: Class<TConcrete>
) : JsonAdapter.Factory {
    private val typeName = interfaceClass.typeName

    override fun create(type: Type, annotations: MutableSet<out Annotation>, moshi: Moshi): JsonAdapter<*>? {
        if (type.typeName != typeName) return null
        val adapter = moshi.adapter<TConcrete>(concreteClass)
        return InterfaceToConcreteAdapter(adapter)
    }
}

Perfect! Now we can have a factory that’ll automatically create the adapter that can handle the concrete type. Now, we could just add the factory manually to the Moshi Builder with the types all drawn out, like so:
Moshi.Builder()
   .add(InterfaceToConcreteAdapterFactory(MammalInterface::class.java, Human::class.java))
   .build()
However, this is a bit cumbersome, instead we can leverage reified types and extensions that Kotlin offer to make the syntax a little cleaner:
/** Add a mapping from [TInterface] to [TConcrete] so that when [TInterface] is encountered, it'll be serialised and deserialised using [TConcrete] */
inline fun <reified TInterface, reified TConcrete : TInterface> Moshi.Builder.addInterfaceToConcreteMap(): Moshi.Builder {
    add(InterfaceToConcreteAdapterFactory(TInterface::class.java, TConcrete::class.java))
    return this
}
We can then leverage this extension method when adding the lookup:
Moshi.Builder()
    .addInterfaceToConcreteMap<MammalInterface, Human>()
    .build()

Now we can add as many mappings as required to Moshi simply and easily using the builder.