Как избежать ошибки, я сделал некоторый поиск в Интернете, и поскольку java 8 изменил свой тип набора ключей, я получил ошибку. это любое решение.
Я использую maven, а плагин животного-сниффера дает эту ошибку с ошибкой сигнатуры.
Ответ 2
Вы можете использовать chm.keySet()
с ConcurrentHashMap
.
Между Java 7 и Java 8 метод ConcurrentHashMap.keySet()
изменился с возврата Set<K>
на возврат ConcurrentHashMap.KeySetView<K,V>
. Это ковариантное переопределение, поскольку KeySetView<K,V>
реализует Set<K>
. Он также совместим как с исходным, так и с двоичным кодом. То есть, тот же исходный код должен работать нормально при построении и запуске на 7, а когда он построен и запущен на 8. Бинарный файл, построенный на 7, должен также работать на 8.
Почему ссылка undefined? Я подозреваю, что есть проблема с конфигурацией сборки, которая смешивает биты с Java 7 и Java 8. Обычно это происходит из-за того, что при попытке компиляции для предыдущей версии указываются опции -source
и -target
, но опция -bootclasspath
не указывается. Например, рассмотрим следующее:
/path/to/jdk8/bin/javac -source 1.7 -target 1.7 MyClass.java
Если MyClass.java содержит любые зависимости от API JDK 8, этот не работает при запуске с использованием JDK 7; это приведет к выбросу NoSuchMethodError
во время выполнения.
Обратите внимание, что эта ошибка не будет возникать немедленно; это произойдет только тогда, когда будет предпринята попытка вызвать метод, о котором идет речь. Это связано с тем, что связь в Java выполняется лениво. Таким образом, вы можете иметь ссылки на несуществующие методы, скрывающиеся в вашем коде в течение произвольного промежутка времени, и ошибки не будут возникать, если путь выполнения не попытается вызвать такой метод. (Это и благословение, и проклятие.)
Не всегда легко определить зависимости от новых API-интерфейсов JDK, посмотрев исходный код. В этом случае, если вы скомпилируете JDK 8, вызов keySet()
будет компилироваться в ссылке на метод, который возвращает ConcurrentHashMap.KeySetView
, потому что тот метод, который найден в библиотеке классов JDK 8. Опция -target 1.7
делает результирующий файл класса совместимым с JDK 7, а параметр -source 1.7
ограничивает уровень языка JDK 7 (который в данном случае не применяется). Но результат на самом деле не является ни рыбой, ни птицей: файл класса будет работать с JDK 7, но он содержит ссылки на библиотеку классов JDK 8 . Если вы попытаетесь запустить это на JDK 7, конечно, он не сможет найти новый материал, введенный в 8, поэтому возникает ошибка.
Вы можете попытаться обойти это, изменив исходный код, чтобы избежать зависимостей от новых API JDK. Поэтому, если ваш код таков:
ConcurrentHashMap<K, V> hashMap = ... ;
final Iterator<CLASS_NAME> itr = hashMap.keySet().iterator();
Вы можете изменить его на это:
ConcurrentHashMap<K, V> hashMap = ... ;
final Iterator<CLASS_NAME> itr = ((Map<K, V>)hashMap).keySet().iterator();
Это может быть сделано для работы, но я не рекомендую его. Во-первых, он забирает ваш код. Во-вторых, это совершенно не очевидно, когда такие изменения должны применяться, поэтому трудно сказать, когда вы получили все случаи. В-третьих, это трудно поддерживать, так как причина для этого актера вовсе не очевидна и легко реорганизуется.
Правильное решение - убедиться, что у вас есть среда сборки, основанная на самой старой версии JDK, которую вы хотите поддерживать. Затем двоичный файл должен работать без изменений на последующих JDK. Например, чтобы скомпилировать JDK 7 с помощью JDK 8, выполните следующие действия:
/path/to/jdk8/bin/javac -source 1.7 -target 1.7 -bootclasspath /path/to/jdk7/jre/lib/rt.jar MyClass.java
В дополнение к указанию опций -source
и -target
указание параметра -bootclasspath
ограничивает все зависимости API, найденных в этом месте, что, конечно же, должно соответствовать версии, указанной для других параметров. Это предотвратит любые непреднамеренные зависимости от JDK 8 от ползучести в результирующие двоичные файлы.
В JDK 9 и более поздних версиях добавлена новая опция --release
. Это эффективно устанавливает исходный, целевой и bootclasspath на один и тот же уровень платформы JDK. Например:
/path/to/jdk9/bin/javac --release 7 MyClass.java
При использовании параметра --release
больше не требуется иметь более старую JDK rt.jar
для целей сборки, так как javac
включает таблицы общедоступных API для более ранних выпусков.
**
В более общем плане такая проблема возникает, когда JDK вводит ковариантные переопределения в новой версии JDK. Это произошло в JDK 9 с различными подклассами Buffer
. Например, ByteBuffer.limit(int)
был переопределен и теперь возвращает ByteBuffer
, тогда как в JDK 8 и ранее этот метод был унаследован от Buffer
и вернулся Buffer
. (В этой области было добавлено несколько других ковариантных переопределений.) Системы, скомпилированные на JDK 9 с использованием только -source 8 -target 8
, будут работать в одной и той же проблеме с NoSuchMethodError
. Решение одно и то же: используйте --release 8
.