Exercise: orThrow
In my everyday practice, I've noticed that I sometimes need a function that in the middle of a multiline expression can be used to throw an exception if a value is null
. So I defined it and called it orThrow
. This is how its usage looks like:
fun getUser(userId: String) = userRepository
.getUser(userId)
.orThrow { UserNotFoundException(userId) }
.also { log("Found user: $it") }
.toUserJson()
Your task is to implement orThrow
function. It should throw the exception specified in its lambda argument if the value is null
. Otherwise, it should return the value typed as non-nullable.
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 functional/scope/orThrow.kt. You can find there unit tests.
Once you are done with the exercise, you can check your solution here.
import org.junit.Before
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
// TODO
@Suppress("RedundantNullableReturnType")
class OrThrowTest {
@Test
fun `should throw for null value`() {
val value: String? = null
val exception = RuntimeException("Value is null")
val result = runCatching { value.orThrow { exception } }
assertEquals(exception, result.exceptionOrNull())
}
@Test
fun `should return value for non-null value`() {
val value: String? = "Hello"
val result = value.orThrow { RuntimeException("Value is null") }
assertEquals("Hello", result)
}
private val value: String? = "Hello"
val result = value.orThrow { RuntimeException("Value is null") }
@Test
fun `should specify result type as non-nullable`() {
assertFalse(::result.returnType.isMarkedNullable)
}
}
class UserController(
private val userRepository: UserRepository,
private val logger: Logger,
) {
fun getUser(userId: String) = userRepository
.getUser(userId)
.orThrow { UserNotFoundException(userId) }
.also { logger.log("User with id ${it.id} found") }
.toUserJson()
}
interface UserRepository {
fun getUser(userId: String): User?
}
interface Logger {
fun log(message: String)
}
data class User(
val id: String,
val name: String,
)
data class UserJson(
val id: String,
val name: String,
)
fun User.toUserJson() = UserJson(
id = id,
name = name
)
class UserNotFoundException(userId: String) : Throwable("User $userId not found")
class FakeLogger: Logger {
var messages: List<String> = emptyList()
override fun log(message: String) {
this.messages += message
}
}
class FakeUserRepository() : UserRepository {
var users: List<User> = emptyList()
fun addUser(user: User) {
users += user
}
override fun getUser(userId: String): User? = users
.find { it.id == userId }
}
class UserControllerTest {
private val logger = FakeLogger()
private val userRepository = FakeUserRepository()
private val userController = UserController(userRepository, logger)
@Before
fun cleanup() {
logger.messages = emptyList()
userRepository.users = emptyList()
}
@Test(expected = UserNotFoundException::class)
fun `should throw for non-existing user`() {
userController.getUser("1")
}
@Test
fun `should return user dto for existing user`() {
userRepository.addUser(User("1", "John"))
val result = userController.getUser("1")
assertEquals(UserJson("1", "John"), result)
}
@Test
fun `should log for existing user`() {
userRepository.addUser(User("1", "John"))
userController.getUser("1")
assertEquals(listOf("User with id 1 found"), logger.messages)
}
}
Marcin Moskala is a highly experienced developer and Kotlin instructor as the founder of Kt. Academy, an official JetBrains partner specializing in Kotlin training, Google Developers Expert, known for his significant contributions to the Kotlin community. Moskala is the author of several widely recognized books, including "Effective Kotlin," "Kotlin Coroutines," "Functional Kotlin," "Advanced Kotlin," "Kotlin Essentials," and "Android Development with Kotlin."
Beyond his literary achievements, Moskala is the author of the largest Medium publication dedicated to Kotlin. As a respected speaker, he has been invited to share his insights at numerous programming conferences, including events such as Droidcon and the prestigious Kotlin Conf, the premier conference dedicated to the Kotlin programming language.