I've recently updated my Android project for Android Studio 3. I wanted to support Java 8 language features, so added the following to build.gradle:
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
I then run my app on an Android 8.0.0 device. At runtime I see
java.lang.NoSuchMethodError: No virtual method keySet()Ljava/util/concurrent/ConcurrentHashMap$KeySetView; in class Ljava/util/concurrent/ConcurrentHashMap; or its super classes (declaration of 'java.util.concurrent.ConcurrentHashMap' appears in /system/framework/core-oj.jar)
I gather this is to do with the fact that the signature of keySet()
was changed in Java 8 from returning Set<K>
to returning KeySetView<K,V>
.
The line that has caused the exception looks like this:
for (Long id : mSomeMap.keySet())
KeySetView
implements Set
, it's certainly Iterable
, so whether this line is interpreted as Java 7 or Java 8 I'd have thought it would work either way. My understanding of Java fundamentals is sketchy - what's going on here?
Update
My flaky understanding so far is this:
While Android now supports some Java 8 language features, its API is not identical to Java 8. In particular, Android's implementation of ConcurrentHashMap.keySet()
returns a Set
, while the Java 8 implementation of ConcurrentHashMap.keySet()
returns a KeySetView
.
Somehow, Android Studio has managed to compile my app with the standard Java 8 JDK and therefore at runtime expects to find a method with the signature KeySetView<K,V> keySet()
. However, Android's ConcurrentHashMap
does not have a method with this signature, so I get a NoSuchMethodError
.
I'm no closer to working out how or why Android Studio is building with an incompatible JDK. In Project Structure, 'Use embedded JDK (recommended)' is checked, so I assume Android Studio is building with the JDK that came bundled with it.
Not a solution
Most comments/answers so far have pointed out that I can just declare the ConcurrentHashMap
as a Map
to get around this. That is a workaround, and not a solution. If the underlying problem is that my app is being built with the wrong JDK, then there may be other instances where method signatures diverge, and since I can't live test 100% of code paths in what is a large project, I can't guarantee that more NoSuchMethodErrors
won't be thrown at run time.