home | src | pltl

Moving to Kotlin

Two weeks ago I began migrating Widget for Trello’s enormous code base of 1395 lines of java (and 807 xml layout lines). Three days later I was at 1092 lines of 100% kotlin. That’s a 21% drop in overall size. whoa.

Motivation for this change was nothing but the sheer curiosity I had for the language and how well it works with existing projects. Having a smallish project like that presented the perfect opportunity to check it out.

The general strategy was to start converting classes with no dependencies and work up from there. This seems to have worked pretty well overall.

I’ll start by saying that intellij’s auto conversion to and from java and kotlin actually works. However, it’s usually not intelligent enough to know how to utilize some of the best features of kotlin and so, some manual tinkering is required.

Another important point is that the widget is already read-only so moving to kotlin’s immutable by default style wasn’t a problem.

The first hurdle I faced was with one of the first attempts at conversion. It’s a simple DialogPreference so what could go wrong?

public class ColorPreference extends DialogPreference {
    int color = DEFAULT_VALUE;

    public ColorPreference(Context context, AttributeSet attributeSet) {
        super(context, attributeSet);
    }
}

Knowing that kotlin can handle default values in constructors and methods in general I assumed that this was an equal representation:

class ColorPreference(
        context: Context, attributeSet: AttributeSet,
        var color: Int = DEFAULT_VALUE) : DialogPreference(context, attributeSet) 

It took me way too long to figure out that because this constructor is called from java, default values don’t work. All I was missing was a @JvmOverloads annotation:

class ColorPreference @JvmOverloads constructor(
        context: Context, attributeSet: AttributeSet,
        var color: Int = DEFAULT_VALUE) : DialogPreference(context, attributeSet) 

Thankfully this was the hardest thing I had to fight with.

One of the best features of kotlin that I immediately started missing in java is extension functions. This is especially useful in android where you need to pass a Context everywhere:

static SharedPreferences getPreferences(Context context) {
    return context.getSharedPreferences(INTERNAL_PREFS, Context.MODE_PRIVATE);
}

Notice the use of preferences() inside MainActiviy which is a Context:

fun Context.preferences() = getSharedPreferences(INTERNAL_PREFS, Context.MODE_PRIVATE)

...

class MainActivity : Activity() {
    fun putString(intent: Intent) {
        preferences().edit()
                .putString(TOKEN_PREF_KEY, intent.data.fragment)
                .commit()
    }
}

You might have also noticed the use of android extensions there, which are indeed nice to have.

Mixing android extensions and the limited pattern matching of the when structure allows this:

override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) {
    when (parent) {
        boardSpinner -> boardSelected(parent, position)
        listSpinner -> list = parent.getItemAtPosition(position) as BoardList
    }
}

Where board and list spinner are ids of spinners defined in some xml somewhere.

Doing the same thing in java would have required some if-else-ing that I didn’t even consider. Instead I went with two inner classes who’s implementation i’d spare from you. They are long dead now.

String templates are another neat addition (which look a lot better with syntax highlighting btw):

public String buildURL() {
    String token = preferences.getString(TrelloAPIUtil.TOKEN_PREF_KEY, "");
    return BASE_URL + API_VERSION + "%s" + KEY + "&" + token;
}

public String user() {
    return String.format(buildURL(), USER);
}

fun buildURL(query: String) = "$BASE_URL$API_VERSION$query$KEY&${preferences.getString(TOKEN_PREF_KEY, "")}"

fun user() = buildURL(USER)

Of course this only works with string literals. Otherwise, String.format remains the way to go.

And yes, lambdas are still pretty cool:

@Override
public void onCreate() {
    startScheduleAlarmThread();
}

private void startScheduleAlarmThread() {
    new Thread(new Runnable() {
        public void run() {
            scheduleAlarm(TrelloWidget.this);
        }
    }).start();
}
override fun onCreate() {
    Executors.callable { scheduleAlarm(this@TrelloWidget) }.call()
}

On a more negative note, kotlin is currently missing a multi-catch syntax:

String get(RequestFuture<String> future) {
    try {
        return future.get();
    } catch (ExecutionException | InterruptedException e) {
        return logException(e);
    }
}
private fun get(future: RequestFuture<String>) = try {
    future.get()
} catch (e: ExecutionException) {
    logException(e)
} catch (e: InterruptedException) {
    logException(e)
}

However, it is planned for a future release. I hope.

And yeah… that’s mostly it.

Anyway, turns out kotlin is really easy to work with. Having intellij fully support it makes a big difference and it seems that most of it’s features directly target java’s notorious verbosity. Which is great. I had a lot of fun during the process and I think it says a lot. I would consider kotlin favorably for any future android project, and even for general small-medium projects.