Skip to content

Instantly share code, notes, and snippets.

@g0t4
Created October 15, 2014 22:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save g0t4/30a5d7f5a07146d6bad3 to your computer and use it in GitHub Desktop.
Save g0t4/30a5d7f5a07146d6bad3 to your computer and use it in GitHub Desktop.
Null safety, proving something is null in kotlin and then not needing to deal with it thereafter
// In Kotlin, I can force the source of a reference to ensure it's not null, that way every thing thereafter doesn't have to deal with null references
// This is what I refer to as fixing things up stream, of course when appropriate.
// Typically sources of references don't both to check for null and even if they do, they can't make explicit that they've taken care of that check. So every consumer of that reference theoretically may need to check.
// In this example, there's no reason the userId, password and serverUrl should ever be null. Unfortunately because of interop with java, that's not a contractual guarantee.
// So at the point that I produce these values for my own use, I perfom the check in one place, stop everything if my assumption is not valid, and otherwise be on my merry way never to worry about null references after this point
abstract class Restore(val runner: BuildRunnerContext) {
val logger: SimpleBuildLogger = runner.getBuild().getBuildLogger()
val client: TeamCityClient
{
// Because I throw an exception if the userId is null, I've proven to the compiler that userId is not null
// therefore it infers the type to be String and not String? (a nullable string)
// see the constructor for TeamCityClient below takes String, not String?
//
// Had I not thrown an exception or otherwise proven to the compiler that userId is not null
// then userId would be inferred as String?
// and constructing TeamCityClient would lead to a compilation error
val userId = runner.getBuildParameters().getAllParameters().get("system.teamcity.auth.userId")
?: throw NullPointerException("system.teamcity.auth.userId should not be null")
val password = runner.getBuildParameters().getAllParameters().get("system.teamcity.auth.password")
?: throw NullPointerException("system.teamcity.auth.password should not be null")
val serverUrl = runner.getConfigParameters().get("teamcity.serverUrl")
?: throw NullPointerException("teamcity.serverUrl should not be null")
client = TeamCityClient(serverUrl, userId, password, logger)
}
abstract fun restore()
}
public class TeamCityClient(server: String, username: String, password: String, private val logger: SimpleBuildLogger) {
// I never have to worry about nulls in here
}
@abreslav
Copy link

Note that you can re-use the exception logic by introducing an extension function:

fun Parameters.safeGet(name: String): Any = this.get(name) ?: throw NullPointerException("$name should not be null")

val userId = runner.getBuildParameters().getAllParameters().safeGet("system.teamcity.auth.userId")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment