This post originally appeared on my blog, Gatling.XYZ. You can check
out the original post here and maybe you'll find something else you like, too.
๐จ๐พโ๐ป And now, without further ado...
Once I tried Kotlin, I was hooked. There is just so much to it that I can't live without anymore, one of which being Kotlin Extension Functions, or just Extensions. If you're unfamiliar with Kotlin, Extensions are a way to add more methods (and even properties) to another class without actually modifying that class. This is useful in the case of wanting to add additional functionality to a third-party library you don't have access to. I've come up with a few that have been invaluable to me, and I use them every day.
1. View.show()
fun View.show(show: Boolean) {
this.visibility = if (show) View.VISIBLE else View.GONE
}
My codebase is set up with MVP and I frequently make calls to my view to show and hide progress spinners when I make API calls. In my View, I can quickly show or hide a view: progressSpinner.show(true)
Tada! I rarely use View.INVISIBLE
so there's no need to attempt to handle that case; if I were to need it, I'd specifically code for that scenario when it arises.
2. Throwable.log()
fun Throwable.log() {
if (this !is SocketTimeoutException && this !is UnknownHostException && this !is HttpException) {
Timber.e(this, this.message)
Crashlytics.logException(this)
}
}
As part of RxKotlin, when I subscribe to an API call I supply it with two consumers: one for success (this::onGetDataSuccess()
) and another for error/failure (this::onGetDataFailure()
). The error consumers must take a Throwable parameter, and the very first line of that function is normally the same for me. Here's a quick example of what an error consumer looks like for me:
private fun onGetDataFailure(throwable: Throwable) {
throwable.log()
// one two skip a few...
}
And now my Throwable has been logged to my loggers of choice with one simple line.
3. LocalDate.toEpochMili() && LocalDateTime.toEpochMili()
fun LocalDate.toEpochMilli(zoneId: ZoneId = ZoneId.systemDefault()): Long {
return this.atStartOfDay().atZone(zoneId).toInstant().toEpochMilli()
}
fun LocalDateTime.toEpochMilli(zoneId: ZoneId org.threeten.bp.ZoneId.systemDefault()): Long {
return this.atZone(zoneId).toInstant().toEpochMilli()
}
We use LocalDate and LocalDateTime in our application (the Threeten version, not the standard, but this could presumably work with that as well), and it amazed me that there wasn't a simple way to turn that object into milliseconds. Before these extensions, things were not pretty. But now? Easy peasy.
Bonus: Here is its lesser-used cousin, Long?.toLocalDateTime()
:
fun Long?.toLocalDateTime(zoneId: ZoneId? = ZoneId.systemDefault()): LocalDateTime? {
if (this == null) {
return null
} else {
return Instant.ofEpochMilli(this).atZone(zoneId).toLocalDateTime()
}
}
And to get a LocalDate from this LocalDateTime, call .toLocalDate()
on the LocalDateTime object.
4. T?.orNull()
fun <T> T?.orNull() = listOf(null, this).random()
I know what you're thinking. "Why?????" First, I'll start by saying I only use this one in my unit tests. When I'm making an object and stuffing it with random data, sometimes that data can be null in the real world. So, when I make my Fakers (just an Object class with a method that creates a "fake" version of a real object using random variables), I sometimes set up a variable to return null instead of an actual value. Here's a quick example:
object class SomeDataFaker {
fun basic() = SomeData(
name = Randomizer.name(),
text = Randomizer.sentence().orNull(),
moreData = MoreDataFaker().basic().orNull()
)
}
The name
must not be null, but the other two can be, and my code should be able to handle any oddities should either of those come back null. If I need to handle specific cases I make sure to set the variable to have a value or null, but when I just need some data that could be anything, throwing some nulls in there gives me some peace of mind that the code will be able to handle them.
How to make your own
Making an Extension Function is easy. You can make them private for a given class, or public for your entire application to use. When I make my Extentions, I make them in an appropriately named file specific(-ish) to whatever class I'm extending. This makes it easier to find when I have to bravely go into legacy code territory that's still in Java. Here's a quick example:
# in a file called ClassImExtendingExtensions.kt
fun ClassImExtending.functionName(someParam: String) {
// "this" refers to the instance of ClassImExtending
// You can do all kinds of things here, as you've seen above.
this.existingVariable = someParam
}
Are there any custom extension functions you use regularly,? Any tips on improving mine? Let me know by leaving a comment or contacting me on Twitter @gatlingxyz.
Top comments (4)
I loved this tip , true i spend a lot of time writing the same long code , what is the benefit of Kotlin if it doesn't make my code easier.
Exaaacttllyy! I kind of wish I had more to share, lol. I think I need to look at what pieces of code I regularly write out repetitively and see if I can turn that into an extension.
Yes, until you write a whole app in one line LOL!
EVEN BETTER! lmfao