Недавно я обновил свой проект Android для Android Studio 3. Я хотел поддерживать функции языка Java 8, поэтому добавил в build.gradle следующее:
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
Затем я запускаю свое приложение на устройстве Android 8.0.0. Во время выполнения я вижу
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)
Я понимаю, что это связано с тем фактом, что подпись keySet()
была изменена в Java 8 с возврата Set<K>
на возврат KeySetView<K,V>
.
Строка, вызвавшая исключение, выглядит так:
for (Long id : mSomeMap.keySet())
KeySetView
реализует Set
, это определенно Iterable
, так что интерпретируется ли эта строка как Java 7 или Java 8, я думаю, что она будет работать в любом случае. Мое понимание основ Java поверхностно - что здесь происходит?
Обновить
Мое шаткое понимание до сих пор таково:
Хотя Android теперь поддерживает некоторые функции языка Java 8, его API не идентичен Java 8. В частности, реализация ConcurrentHashMap.keySet()
в Android возвращает Set
, а реализация ConcurrentHashMap.keySet()
в Java 8 возвращает KeySetView
.
Каким-то образом Android Studio удалось скомпилировать мое приложение со стандартным JDK Java 8, и поэтому во время выполнения он ожидает найти метод с сигнатурой KeySetView<K,V> keySet()
. Однако в Android ConcurrentHashMap
нет метода с такой сигнатурой, поэтому я получаю NoSuchMethodError
.
Я не приблизился к тому, чтобы понять, как и почему Android Studio использует несовместимый JDK. В структуре проекта установлен флажок «Использовать встроенный JDK (рекомендуется)», поэтому я предполагаю, что Android Studio строится с JDK, который поставляется вместе с ним.
Не решение
В большинстве комментариев/ответов до сих пор указывалось, что я могу просто объявить ConcurrentHashMap
как Map
, чтобы обойти это. Это обходной путь, а не решение. Если основная проблема заключается в том, что мое приложение создается с использованием неправильного JDK, то могут быть и другие случаи, когда сигнатуры методов расходятся, и, поскольку я не могу живой тест 100% путей кода в большом проекте, я не могу гарантировать, что больше NoSuchMethodErrors
не будет выброшено во время выполнения.
mSomeMap
этоjava.util.concurrent.ConcurrentHashMap
, извините, что не указал это явно. - person UtterlyConfused   schedule 23.08.2017keySet
в CHM Android не возвращаетKeySetView
, см. android.googlesource.com/platform/libcore/+/master/ojluni/src/ Откуда взялась строкаfor (Long id : mSomeMap.keySet())
- это ваш код или это часть скомпилированной библиотеки, которую вы используете? - person Stefan Zobel   schedule 23.08.2017retrolambda
. Несмотря на то, что исходный код этой библиотеки не содержал никаких ссылок наKeySetView
, на этот тип ссылались в файле класса, который использовал классCHM
. В моем случае решением было изменить типstatic
дляmSomeMap
в этой библиотеке сCHM
наMap
. - person Stefan Zobel   schedule 23.08.2017KeySetView
, а мой код никогда явно не упоминаетKeySetView
, то откуда вообще взялось ожидание, чтоkeySet()
вернетKeySetView
? - person UtterlyConfused   schedule 23.08.2017keySet()Ljava/util/concurrent/ConcurrentHashMap$KeySetView
в сгенерированном javac байтовом коде. Итак, я подозреваю, что вам каким-то образом удается скомпилировать Oracle JRE? - person Stefan Zobel   schedule 23.08.2017static
дляmSomeMap
сCHM
наConcurrentMap
помогает. Но опять же, я считаю, что ваша проблема в том, что вы компилируете Java 8rt.jar
. - person Stefan Zobel   schedule 23.08.2017core-oj.jar
автоматически. Возможно, будет лучше создать свой проект с нуля. - person Stefan Zobel   schedule 23.08.2017mSomeMap
сConcurrentHashMap
наConcurrentMap
. - person Stefan Zobel   schedule 28.11.2017