I used IntelliJ to convert to Kotlin. It gave me the class below. While it compiles and works, it does not look like Kotlin. I would like to take the resulting code and use Kotlin’s features to make it look more like Kotlin
The starting point is this ugly if – which has the lower clauses collapsed to fit in the screenshot. This needs to go.
Templates
My first thought was templates. But after a little bit of scratching around I figures out the problem was not different types. All arrays are of type IntervalData, so that would not bring anything to the table. Maybe I’m wrong. I’ve not done much with templates in any language.
Base Class Method
And I could not try to implement it in the base class either, as I have called each array by different names – minutelyData, hourlyData and dailyData. I don’t want to change that. That conforms to my Objective C upbringing, where you name a variable with what it is, not some cryptic reference to it. Anyway that is not a very Kotliny solution
when
If you have an ugly if statement in Kotlin, the obvious starting place is when. This is a pretty cool piece of kit in Kotlin – it’s their answer to switch.
I am watching a pretty cool course on Udacity: Kotlin Bootcamp for Programmers, free at the time of writing. It doesn’t nauseate you with trying to describe what a variable is. Every single concept is backed up by exercises, and it is 50% video and 50% exercises. Lesson 3 ‘Functions’, see ’14. Compact Functions’ where he takes a when statement, and ships out the logic to other variables and methods. Pretty cool. I’d like to do this if I can.
I have got rid of the ugly if. . I’ve added a method called timeTilRain which takes in a time multiplier for number of minutes. But this is still not very readable, and it calculates the result twiceMore work required:
indexOfFirst
There is a function called ‘periodWhenIntensityExceeded’ which uses a while loop. This looks like it needs a lambda:
Kotlin Ternary Operator
It’s slightly different in Kotlin – just a case of remembering. Starts with the keyword if, and no curly braces.
Or let’s go one better and use the Elvis operator ?: This is for providing a default in the case of null.
precipIntensity = jsonFloatValueFor(WeatherConstants.PRECIP_INTENSITY, jsonHourly) ?: 0f
Kotlin Class Properties
You can declare the properties directly in the class parameters, by adding val or var before the variable name. Modifiers such as private, protected etc included.
Kotlin Playgound
The playground is really nice for trying out code. If you used it before on a Chromium browser and hated it, try it now. They’ve sorted out the viewing. It now is really a good experience. Nice one guys. https://play.kotlinlang.org/
Starting Code
Here is my starting class, as provided by IntelliJ when I clicked Convert to Kotlin.
package com.sners.snowforecast.data import java.util.ArrayList //precipIntensity: A numerical value representing the average expected intensity (in inches of liquid water per hour) // of precipitation occurring at the given time conditional on probability (that is, assuming any precipitation occurs at all). // A very rough guide is that a value of 0 in./hr. corresponds to no precipitation, // 0.002 in./hr. corresponds to very light precipitation, // 0.017 in./hr. corresponds to light precipitation, // 0.1 in./hr. corresponds to moderate precipitation, // and 0.4 in./hr. corresponds to heavy precipitation. /** * Everything about Precipitation * @param inDaily daily weather data * @param inHourly hourly weather data * @param inMinutely minutely weather data * @param inCurrently current weather data */ class Precipitation(inDaily: Daily?, inHourly: Hourly?, inMinutely: Minutely?, inCurrently: Currently) { /** * @property daily Daily weather info */ private val daily: Daily? /** * @property hourly Hourly weather info */ private val hourly: Hourly? /** * @property minutely Minutely weather info */ private val minutely: Minutely? /** * @property currently Current weather info */ private val currently: Currently /** * @property weatherHelper Helper class with useful weather functions */ private val weatherHelper = WeatherHelper() /** * @property minPrecipLight Min precip to take into account */ private val minPrecipLight = 0.002 /** * @property minPrecipMedium Min precip to take into account if we're ignoring light precip */ private val minPrecipMedium = 0.017 init { daily = inDaily hourly = inHourly minutely = inMinutely currently = inCurrently } /** * Time til Precipitation * @param toIgnoreLightPrecip boolean * @return Minutes til precipitation */ private fun timeTil(toIgnoreLightPrecip: Boolean): Long { var minutesTilPrecip = -1 val minPrecip = if (toIgnoreLightPrecip) minPrecipMedium else minPrecipLight val precipIntensity: Float = currently.precipIntensityNum if (precipIntensity > minPrecip) { // It's raining now minutesTilPrecip = 0 } else { // Check minutely if (null != minutely) { val rainMinutes = periodWhenIntensityExceeded(minutely.minutelyData,minPrecip) if (rainMinutes >= 0) { minutesTilPrecip = (rainMinutes + 1) } } if (minutesTilPrecip < 1) { // Check hourly if (null != hourly) { val rainHours: Int = periodWhenIntensityExceeded(hourly.hourlyData, minPrecip) if (rainHours >= 0) { minutesTilPrecip = ((rainHours + 1) * 60) } } } if (minutesTilPrecip < 1) { // Check hourly if (null != daily) { val rainDays: Int = periodWhenIntensityExceeded(daily.dailyData, minPrecip) minutesTilPrecip = if (rainDays >= 0) { ((rainDays + 1) * 60 * 24) } else { -1 } } } } return minutesTilPrecip.toLong() } /** * Time til Precipitation as string * @param toIgnoreLightPrecip boolean * @return Formatted time string */ fun timeTilString(toIgnoreLightPrecip: Boolean): String? { var timeTilString: String? = WeatherConstants.NONE_FORECAST val timeTil = timeTil(toIgnoreLightPrecip) if (timeTil > 0) { timeTilString = weatherHelper.formatTime(timeTil) } else if (timeTil == 0L) { timeTilString = WeatherConstants.NOW } return timeTilString } /** * Period when Intensity Exceeded * @param intervalData the data to search * @param minValue The minimum value under which we ignore * @return Integer index of element where condition was first met */ private fun periodWhenIntensityExceeded(intervalData: ArrayList<IntervalData>, minValue: Double): Int { var periodFound = -1 var precipExceedsMin = false var intervalCounter = 0 while (intervalCounter < intervalData.size && !precipExceedsMin) { val fieldValue = intervalData[intervalCounter].precipIntensity if (fieldValue > minValue) { precipExceedsMin = true periodFound = intervalCounter } intervalCounter++ } return periodFound } }`