article banner

Exercise: Generic Response

You needed to model a response from a server that can be represented as a success or a failure, both of which contain data of a generic type. This is how you modeled it:

sealed class Response<R, E> class Success<R, E>(val value: R) : Response<R, E>() class Failure<R, E>(val error: E) : Response<R, E>()

However, you found that this implementation is problematic. To create a Success object, you need to provide two generic types, but you only need one. To create a Failure object, you need to provide two generic types, but you only need one. Your task is to fix this problem.

val rs1 = Success(1) // Compilation error val rs2 = Success("ABC") // Compilation error val re1 = Failure(Error()) // Compilation error val re2 = Failure("Error") // Compilation error

You need to define Success and Failure in a way that each can be created with only one generic type argument. You want to be able to use Success and Failure without specifying generic types, so just use Success(1) or Failure("Error").

You also want to allow Success<Int> to be upcast to Success<Number> or to Success<Any>, and Failure<Error> to be upcast to Failure<Throwable> or to Failure<Any>. You want to be able to use Success<Int> as Response<Int, Throwable>.

val rs1 = Success(1) val rs2 = Success("ABC") val re1 = Failure(Error()) val re2 = Failure("Error") val rs3: Success<Number> = rs1 val rs4: Success<Any> = rs1 val re3: Failure<Throwable> = re1 val re4: Failure<Any> = re1 val r1: Response<Int, Throwable> = rs1 val r2: Response<Int, Throwable> = re1

This problem can either be solved in the below playground or you can clone kotlin-exercises project and solve it locally. In the project, you can find code template for this exercise in advanced/generics/Response.kt. You can find there starting code and example usage.

Once you are done with the exercise, you can check your solution here.

Playground

sealed class Response<R, E> class Success<R, E>(val value: R) : Response<R, E>() class Failure<R, E>(val error: E) : Response<R, E>() fun usage() { val rs1 = Success(1) val rs2 = Success("ABC") val re1 = Failure(Error()) val re2 = Failure("Error") val rs3: Success<Number> = rs1 val rs4: Success<Any> = rs1 val re3: Failure<Throwable> = re1 val re4: Failure<Any> = re1 val r1: Response<Int, Throwable> = rs1 val r2: Response<Int, Throwable> = re1 }