40

I just noticed this lint error:

Call requires API Level 24 (current min is 19) java.util.map#foreach

when I use the extension function forEach on a MutableMap in Kotlin. This didn't happen when I wrote the line, but its there now. And I'm not seeing this error on my other machine.

Ahmed Ashour
  • 5,179
  • 10
  • 35
  • 56
Sebastian Rüttger
  • 765
  • 3
  • 10
  • 16
  • Try invalidate cache and run `gradle clean` since your other machine isn't getting that error. Also check Naetmul's anwser below. – glee8e Jun 26 '17 at 01:59
  • Whilst Naetmul's answer seems to be the cause of your issue, I'm currently seeing a lot of these warnings for no reason in AS3.0 anyway, even in things like `Bundle#putBoolean`. Linting seems a bit broken currently. – ditn Jun 26 '17 at 14:47
  • 13
    In case you recently pass by here after migrating to Kotlin 1.5.0, there is a Lint bug in AGP 4.1, causing a similar issues. Please refer to https://issuetracker.google.com/issues/185418482 for details. TL;DR you need to update to AGP 4.2.0 first – Konsumierer May 19 '21 at 14:28
  • Not resolved to this date even with the latest version of AGP. Thanks a lot Konsumierer for the issuetracker link, will follow this closely. – Charly Lafon Dec 03 '21 at 15:55
  • This issue reappears in Kotlin 1.9.x, for people coming here due to that: the answers below still work – avalancha Aug 29 '23 at 11:05

4 Answers4

83

What you are using is not kotlin.collections.MutableMap.forEach.

What you are using seems to be Map.forEach in Java 8.

Refer to this article: http://blog.danlew.net/2017/03/16/kotlin-puzzler-whose-line-is-it-anyways/

This seems to be a common mistake.

Java 8 is well-supported from Android API level 24.

For short, do not use forEach like map.forEach { t, u -> Log.i("tag", t + u) } (this is using Java 8 API, which does not work for Android API level <= 23).
Use it like map.forEach { (t, u) -> Log.i("tag", t + u) } (this is using Kotlin API).
(Not two parameters. Just one parameter that is a pair.)

Naetmul
  • 14,544
  • 8
  • 57
  • 81
  • 2
    this solved it for me. The same thing is happening for replace() on MutableMap, so im guessing this is the same cause – Sebastian Rüttger Jun 26 '17 at 08:15
  • 1
    double accepted answer. this allows me to use forEach on kotlin. – mochadwi Feb 20 '19 at 13:13
  • The file is entirely written in Kotlin, and it still gives this error. There's no way to be using Java 8 from Kotlin. – IgorGanapolsky Jul 12 '19 at 16:03
  • 2
    @IgorGanapolsky Typically you're using Kotlin with some Java libraries, so the Java version of those libraries may matter. The two arg version of j.u.Map.forEach is from Java 8 onwards, i.e., Android API 24 onwards. – Ricky Clarkson May 04 '21 at 05:05
  • @Naetmul what is the relation between java language features and android api levels? – Sourav Kannantha B May 06 '21 at 08:00
  • @SouravKannanthaB Android API Level <= 23: Java 7, Android API Level >= 24: Subset of Java 8, https://developer.android.com/studio/write/java8-support.html It seems that Google does not try to include Java 9 language features to Android. Just use Kotlin instead. – Naetmul May 11 '21 at 06:32
  • BTW, I found the same issue reappeared after the kotlin 1.5.x upgrade. It was a problem that is already fixed in 4.2: https://issuetracker.google.com/issues/185418482 – neworld Jun 15 '21 at 12:35
  • I am using the single argument version and this problem just appeared for me. ref `event.attributes?.forEach { (key, value) -> if (key != null && value != null) { put(key, value) } }` – Bill Mote Jul 29 '21 at 14:22
  • I have like 30 places where I get this error and can't manage to make it work with this solution. I have to use import kotlin.collections.forEach as kForEach and then use kForEach instead of forEach – Damia Fuentes Aug 27 '21 at 18:29
11

This also happens in a list even tho I'm using I need to use:

list.iterator().forEach {}

the key is use iterator() before forEach.

Javier
  • 1,469
  • 2
  • 20
  • 38
  • This solved it for me. I am using the single argument version as mentioned in another answer (ref. `event.attributes?.forEach { (key, value) -> if (key != null && value != null) { put(key, value) } }`). – Bill Mote Jul 29 '21 at 14:25
2

Ran into this because the map a dependency provided was a Java 8 map, so the forEach was linted as the Java 8 version, no matter how I grouped my parameters in the lambda signature (one, two -> vs (one, two) ->). Best solution I could find was using an explicit import with an alias (import kotlin.collections.forEach as kForEach). The alias keeps optimize imports from removing the explicit import.

Carter Hudson
  • 1,176
  • 1
  • 11
  • 23
-4

Map.forEach is supported in Java 8 and its support in Android started from API 24

Shinoo Goyal
  • 601
  • 8
  • 10