Kotlin Collection on Android with Java 8

Peng Jiang
Mar 31, 2020 · 4 min read

We all love Kotlin, Google has chosen it as the first-class language for Android development and the ASOS Android team consider it the only language for new feature implementation.

Since Kotlin is so good, it begs the question of whether we need to consider Java any more? Google has a specific page regarding the Java 8 support on Android Studio and Jake Wharton has written a useful blog piece regarding Java 8 support on Android. With the D8 desugars, Java 8 features lambda, default method and method references which are fully supported for all Android API versions. But some Java 8 features like Stream, Time etc are only supported for Android version 24+. Why do you need to consider the different support for Java 7 and Java 8 on Android if you’re using Kotlin?

Recently we had an issue when running Kotlin collection methods on devices running below Android 24. Here’s a code snippet.

As you can see, the code is very simple; it will return the default resource if the user hasn’t previously set any payment methods. When we ran the code, it passed our UI tests (Our test devices are all 24+) and Unit tests (Java 8). But when we ran the app on a device below 24, it throws a NoSuchMethod exception.

java.lang.NoSuchMethodError: No virtual method getOrDefault(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; in class Ljava/util/EnumMap; or its super classes (declaration of 'java.util.EnumMap' appears in /system/framework/core-libart.jar)

Thankfully the pre-launch test on the Play store identified this issue and sent us an alert.

Why did this error arise given we were using Kotlin? If you check the API document or the source code in Kotlin Github, you’ll find the annotation PlatformDependent and target JDK 1.8 for getOrDefaultmethod. When you decompile the Kotlin code, you can see the method java.util.EnumMap.getOrDefault is used.

The fix is really simple, you can either use Elvis operator ?:

resources[paymentType] ?: default

Or, if you want to use a Kotlin method:

resources.getOrElse(paymentType, {default})

The methodgetOrElse is the same as using Elvis operator. The auto Kotlin converter in Android studio also converts the getOrDefaultmethod using Elvis operator.

@kotlin.internal.InlineOnly
public inline fun <K, V> Map<K, V>.getOrElse(key: K, defaultValue: () -> V): V = get(key) ?: defaultValue()

Fixing the issue is simple, but why is the code platform dependent in the first place? From the document, we can see the platform dependent annotation specifies:

the corresponding built-in method exists depending on platform. Current implementation for JVM looks whether method with same JVM descriptor exists in the module JDK.

If you check the Collection class in Kotlin, two methods are annotated as PlatformDependent: getOrDefault(key, default) and remove(key, value). They are both introduced from Java 8 . Android’s support for this bytecode requires a minimum API of 24 but it will give a lint check warning if your minimum version is below 24. Here’s a very simple snip to call these methods directly in the Java class and the lint check which will show the warning directly in the Android Studio.

When you call it from Kotlin, it will show the same warning if it’s a HashMap class, but it doesn’t show the same warning when you call from an EnumMap or TreeMap. That’s why our CI system doesn’t report this issue.

What can we do to prevent this issue from happening in the future? Pay more attention to the Kotlin collection classes when using them on Android and always test your code on devices running the minimum Android version you supported. In our CI process, we are trying to add minimum support devices to catch the issue in the pull request review state and have nightly build releases in the internal test channel to make full use of the pre-launch test.

My name is Peng Jiang, I’m a Lead Android Engineer at ASOS and am passionate about simple and well-crafted code. In my spare time, I also play with Swift and Flutter. At ASOS, we’re always looking for strong, friendly and talented developers. You’ll only need to write Kotlin here. If that sounds interesting to you, do get in touch!

The ASOS Tech Blog

A collective effort from ASOS's Tech Team, driven and…

Thanks to Victor Ireri, Paul Craciunas, Olivier Bonal, Joe McGuinness, Gareth Waterhouse, and Rosie Tredwell

Peng Jiang

Written by

Work @Huawei on #Android

The ASOS Tech Blog

A collective effort from ASOS's Tech Team, driven and directed by our writers. Learn about our engineering, our culture, and anything else that's on our mind.

Peng Jiang

Written by

Work @Huawei on #Android

The ASOS Tech Blog

A collective effort from ASOS's Tech Team, driven and directed by our writers. Learn about our engineering, our culture, and anything else that's on our mind.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store