Что вызывает java.lang.IncompatibleClassChangeError?

Я упаковываю библиотеку Java как JAR и бросаю много java.lang.IncompatibleClassChangeError, когда пытаюсь вызвать из нее методы. Эти ошибки кажутся случайными. Какие проблемы могут быть причиной этой ошибки?

Ответ 1

Это означает, что вы внесли некоторые несовместимые двоичные изменения в библиотеку без перекомпиляции кода клиента. Спецификация языка Java §13 описывает все такие изменения, наиболее заметно, что static non- static non- частные поля/методы static или наоборот.

Перекомпилируйте клиентский код с новой библиотекой, и вам должно быть хорошо идти.

ОБНОВЛЕНИЕ: если вы публикуете публичную библиотеку, вы должны избегать несовместимых бинарных изменений в максимально возможной степени, чтобы сохранить так называемую "двоичную обратную совместимость". Обновление баннеров зависимостей в идеале не должно нарушать приложение или сборку. Если вам нужно нарушить двоичную обратную совместимость, рекомендуется допустить изменение основного номера версии (например, от 1.xy до 2.0.0).

Ответ 2

Ваша недавно упакованная библиотека не поддерживает обратную двоичную совместимость (BC) со старой версией. По этой причине некоторые из клиентов библиотеки, которые не перекомпилированы, могут вызывать исключение.

Это полный список изменений в API библиотек Java, который может привести к тому, что клиенты, созданные со старой версией библиотеки, будут бросать java.lang. IncompatibleClassChangeError, если они запускаются на новый (т.е. разрыв BC):

  • Не конечное поле становится статическим,
  • Непостоянное поле становится нестатическим,
  • Класс станет интерфейсом,
  • Интерфейс становится классом,
  • Если вы добавите новое поле в класс/интерфейс (или добавьте новый суперкласс/супер-интерфейс), тогда статическое поле из суперинтерфейса клиентского класса C может скрыть добавленное поле (с тем же именем) унаследованный от суперкласса C (очень редкий случай).

Примечание. Существует много других исключений, вызванных другими несовместимыми изменениями: NoSuchFieldError, NoSuchMethodError, IllegalAccessError, InstantiationError, VerifyError, NoClassDefFoundError и AbstractMethodError.

Лучшая статья о BC - это "Разработка Java-интерфейсов 2: Достижение бинарной совместимости API" , написанная Джим де Ривьер.

Для обнаружения таких изменений есть также автоматические инструменты:

Использование japi-соответствия-проверки для вашей библиотеки:

japi-compliance-checker OLD.jar NEW.jar

Использование инструмента clirr:

java -jar clirr-core-0.6-uber.jar -o OLD.jar -n NEW.jar

Удачи!

Ответ 3

Хотя эти ответы правильны, решение проблемы часто бывает более сложным. Как правило, это результат двух слегка разных версий одной и той же зависимости от пути к классам и почти всегда вызван либо другим суперклассом, чем был первоначально скомпилирован против того, чтобы быть на пути к классам, либо какой-либо импорт транзитивного закрытия был другим, но обычно в классе экземпляр и вызов конструктора. (После успешной загрузки класса и вызова ctor вы получите NoSuchMethodException или еще что-то.)

Если поведение кажется случайным, это, скорее всего, результат многопоточной загрузки программ с различными транзитивными зависимостями, основанный на том, какой код попал первым.

Чтобы устранить эти проблемы, попробуйте запустить VM с -verbose в качестве аргумента, а затем посмотрите на классы, которые были загружены при возникновении исключения. Вы должны увидеть удивительную информацию. Например, имея несколько копий одной и той же зависимости и версий, которые вы никогда не ожидали или не приняли бы, если бы знали, что они включены.

Решение дублировать банки с Maven лучше всего выполнять с помощью комбинации maven-dependency-plugin и maven-enforcer-plugin под Maven (или SBT Dependency Graph Plugin), а затем добавляя эти банки в раздел вашего POM верхнего уровня или как импортированную зависимость элементов в SBT (чтобы удалить эти зависимости).

Удачи!

Ответ 4

Я также обнаружил, что при использовании JNI, вызывая метод Java из С++, если вы передадите параметры вызываемому Java-методу в неправильном порядке, вы получите эту ошибку при попытке использовать параметры внутри вызываемого метода (потому что они не будут подходящим типом). Сначала я был ошеломлен тем, что JNI не проверяет вас как часть проверки подписи класса при вызове метода, но я полагаю, что они не делают такого рода проверку, потому что вы можете передавать полиморфные параметры, и они должны предположите, что вы знаете, что делаете.

Пример С++ Код JNI:

void invokeFooDoSomething() {
    jobject javaFred = FredFactory::getFred(); // Get a Fred jobject
    jobject javaFoo = FooFactory::getFoo(); // Get a Foo jobject
    jobject javaBar = FooFactory::getBar(); // Get a Bar jobject
    jmethodID methodID = getDoSomethingMethodId() // Get the JNI Method ID


    jniEnv->CallVoidMethod(javaFoo,
                           methodID,
                           javaFred, // Woops!  I switched the Fred and Bar parameters!
                           javaBar);

    // << Insert error handling code here to discover the JNI Exception >>
    //  ... This is where the IncompatibleClassChangeError will show up.
}

Пример кода Java:

class Bar { ... }

class Fred {
    public int size() { ... }
} 

class Foo {
    public void doSomething(Fred aFred, Bar anotherObject) {
        if (name.size() > 0) { // Will throw a cryptic java.lang.IncompatibleClassChangeError
            // Do some stuff...
        }
    }
}

Ответ 5

У меня была такая же проблема, и позже я понял, что я запускаю приложение на Java версии 1.4, в то время как приложение скомпилировано в версии 6.

На самом деле, причина заключалась в наличии дублированной библиотеки, одна из которых находится внутри пути к классам, а другая - внутри файла jar, который находится внутри пути к классам.

Ответ 6

Другая ситуация, когда эта ошибка может появиться, связана с обложкой кода Эмма.

Это происходит при назначении объекта интерфейсу. Я предполагаю, что это имеет какое-то отношение к объекту, инструментарий, а не к бинарной совместимости.

http://sourceforge.net/tracker/?func=detail&aid=3178921&group_id=177969&atid=883351

К счастью, эта проблема не возникает с Cobertura, поэтому я добавил плагин cobertura-maven в своих плагинах отчетов моего pom.xml

Ответ 7

Я столкнулся с этой проблемой, не разворачивая и передислоцируя войну со стеклянными рыбками. Моя структура класса была такой:

public interface A{
}

public class AImpl implements A{
}

и он был изменен на

public abstract class A{
}

public class AImpl extends A{
}

После остановки и перезапуска домена это получилось отлично. Я использовал стеклянную рыбку 3.1.43

Ответ 8

У меня есть веб-приложение, которое отлично отлично работает на моем локальном компьютере tomcat (8.0.20). Однако, когда я помещал его в среду qa (tomcat - 8.0.20), он продолжал давать мне IncompatibleClassChangeError, и он жаловался, что я расширялся на интерфейсе. Этот интерфейс был изменен на абстрактный класс. И я скомпилировал родительский и дочерний классы, и все же я продолжал получать ту же проблему. Наконец, я хотел отлаживать, поэтому я изменил версию родителя на x.0.1-SNAPSHOT, а затем скомпилировал все, и теперь он работает. Если кто-то все еще сталкивается с проблемой после следующих ответов, пожалуйста, убедитесь, что версии в вашем pom.xml также верны. Измените версии, чтобы увидеть, работает ли это. Если да, то исправьте проблему с версией.

Ответ 9

Мой ответ, я считаю, будет Intellij специфическим.

Я перестроил чистую, даже до тех пор, пока не удалил вручную "out" и "target" dirs. Intellij имеет "недействительные кеши и перезапуск", который иногда очищает нечетные ошибки. На этот раз это не сработало. Все версии зависимостей выглядят правильно в меню настроек проекта → modules.

Последний ответ заключался в том, чтобы вручную удалить мою проблему из моего локального репозитория maven. Старая версия bouncycastle была виновницей (я знал, что я только что изменил версии, и это будет проблемой), и хотя в старой версии не было места, где строилось, она решила мою проблему. Я использовал intellij версии 14, а затем был обновлен до 15 во время этого процесса.

Ответ 10

В моем случае я столкнулся с этой ошибкой таким образом. pom.xml моего проекта определены две зависимости A и B. И как A, так и B определяют зависимость от одного и того же артефакта (назовите его C), но разные его версии (C.1и C.2). Когда это произойдет, для каждого класса в C maven может выбрать только одну версию класса из двух версий (при построении uber-jar). Он выберет "ближайшую" версию на основе ее правил посредничества и выведет предупреждение "У нас есть повторяющийся класс..." . Если между версиями изменяется подкласс метода/класса, это может вызвать исключение java.lang.IncompatibleClassChangeError, если во время выполнения используется некорректная версия.

Дополнительно: Если A должен использовать v1 из C, а B должен использовать v2 из C, тогда мы должны переместить C in A и B poms, чтобы избежать конфликта классов (у нас есть предупреждение о повторяющемся классе) при построении окончательного проекта, который зависит как от A, так и B.

Ответ 11

В моем случае ошибка появилась, когда я добавил библиотеку com.nimbusds в свое приложение, развернутое на Websphere 8.5.
Произошло следующее исключение:

Вызывается: java.lang.IncompatibleClassChangeError: org.objectweb.asm.AnnotationVisitor

Решение состояло в том, чтобы исключить asm jar из библиотеки:

<dependency>
    <groupId>com.nimbusds</groupId>
    <artifactId>nimbus-jose-jwt</artifactId>
    <version>5.1</version>
    <exclusions>
        <exclusion>
            <artifactId>asm</artifactId>
            <groupId>org.ow2.asm</groupId>
        </exclusion>
    </exclusions>
</dependency>

Ответ 12

Пожалуйста, проверьте, не содержит ли ваш код два проекта модулей, которые имеют одинаковые определения классов и пакетов. Например, это может произойти, если кто-то использует copy-paste для создания новой реализации интерфейса на основе предыдущей реализации.

Ответ 13

Если это запись возможных случаев этой ошибки, то:

Я только что получил эту ошибку в WAS (8.5.0.1) во время загрузки конфигурации spring (3.1.1_release) CXF (2.6.0), где исключение BeanInstantiationException запустило исключение CXF ExtensionException, свернувшее IncompatibleClassChangeError. Следующий фрагмент показывает суть трассировки стека:

Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.apache.cxf.bus.spring.SpringBus]: Constructor threw exception; nested exception is org.apache.cxf.bus.extension.ExtensionException
            at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:162)
            at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:76)
            at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:990)
            ... 116 more
Caused by: org.apache.cxf.bus.extension.ExtensionException
            at org.apache.cxf.bus.extension.Extension.tryClass(Extension.java:167)
            at org.apache.cxf.bus.extension.Extension.getClassObject(Extension.java:179)
            at org.apache.cxf.bus.extension.ExtensionManagerImpl.activateAllByType(ExtensionManagerImpl.java:138)
            at org.apache.cxf.bus.extension.ExtensionManagerBus.<init>(ExtensionManagerBus.java:131)
            [etc...]
            at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:147)
            ... 118 more

Caused by: java.lang.IncompatibleClassChangeError: 
org.apache.neethi.AssertionBuilderFactory
            at java.lang.ClassLoader.defineClassImpl(Native Method)
            at java.lang.ClassLoader.defineClass(ClassLoader.java:284)
            [etc...]
            at com.ibm.ws.classloader.CompoundClassLoader.loadClass(CompoundClassLoader.java:586)
            at java.lang.ClassLoader.loadClass(ClassLoader.java:658)
            at org.apache.cxf.bus.extension.Extension.tryClass(Extension.java:163)
            ... 128 more

В этом случае решение заключалось в том, чтобы изменить порядок пути к модулю модуля в моем военном файле. То есть, откройте приложение войны в консоли WAS и выберите клиентский модуль (модули). В конфигурации модуля установите для параметра class-loading значение "parent last".

Это находится в консоли WAS:

  • Applicatoins → Типы приложений → WebSphere Enterprise Applications
  • Нажмите ссылку, представляющую ваше приложение (война)
  • Нажмите "Управление модулями" в разделе "Модули".
  • Нажмите ссылку для базового модуля (ов)
  • Измените "Порядок загрузчика класса" как "(последний родительский элемент" ).

Ответ 14

Документирование другого сценария после сжигания слишком много времени.

Убедитесь, что у вас нет контейнера зависимостей, у которого есть класс с аннотацией EJB.

У нас был общий файл jar с аннотацией @local. Затем этот класс был перенесен из этого общего проекта и в наш основной проект jjb jar. Наш кувшин ejb и наша общая банка объединены в ухо. Версия нашей общей зависимости jar не обновлялась. Таким образом, 2 класса пытаются быть чем-то с несовместимыми изменениями.

Ответ 15

Все вышеперечисленное - по какой-то причине я делал большой рефакторинг и начал это делать. Я переименовал пакет, в котором находился мой интерфейс, и который очистил его. Надеюсь, что это поможет.

Ответ 16

По какой-то причине одно и то же исключение также генерируется при использовании JNI и передаче аргумента jclass вместо jobject при вызове Call*Method().

Это похоже на ответ от Псалом 33 Огра.

void example(JNIEnv *env, jobject inJavaList) {
    jclass class_List = env->FindClass("java/util/List");

    jmethodID method_size = env->GetMethodID(class_List, "size", "()I");
    long size = env->CallIntMethod(class_List, method_size); // should be passing 'inJavaList' instead of 'class_List'

    std::cout << "LIST SIZE " << size << std::endl;
}

Я знаю, что немного поздно ответить на этот вопрос через 5 лет после того, как его спросили, но это один из лучших хитов при поиске java.lang.IncompatibleClassChangeError, поэтому я хотел документировать этот специальный случай.

Ответ 17

Добавление двух центов. Если вы используете scala и sbt и scala -logging как зависимость, тогда это может произойти, потому что scala -logging более ранняя версия имела имя scala -logging-api.So, по существу, разрешения зависимостей не происходят из-за разных имен, приводящих к ошибкам во время запуска при запуске приложения scala.

Ответ 18

Другой причиной этой проблемы является использование Instant Run для Android Studio.

Исправление

Если вы обнаружите, что вы начали получать эту ошибку, отключите Instant Run.

  • Основные настройки Android Studio
  • Сборка, выполнение, развертывание
  • Мгновенный запуск
  • Отключить "Включить мгновенный запуск..."

Почему

Instant Run изменяет большое количество вещей во время разработки, чтобы быстрее обновлять текущее приложение. Следовательно, мгновенный запуск. Когда это работает, это действительно полезно. Однако, когда проблема, подобная этой проблеме, лучше всего отключить Instant Run до следующей версии Android Studio.