Что вызывает java.ленг.IncompatibleClassChangeError?

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

17 ответов


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

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

UPDATE: если вы публикуете публичную библиотеку, вы должны избегать создания несовместимых двоичных файлов изменения как можно больше, чтобы сохранить то, что известно как "двоичная обратная совместимость". Обновление только банок зависимостей в идеале не должно нарушать приложение или сборку. Если вам нужно сломать двоичную обратную совместимость, это рекомендовано увеличение старшего номера версии (например, с 1.X. y до 2.0.0) перед выпуском изменения.


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

Это полное список изменений в API библиотеки Java, которые могут привести к клиентам, построенным со старой версией библиотеки, чтобы бросить java.ленг.IncompatibleClassChangeError если они работают на новом (т. е. ломать До н. э.):

  1. non-final поле становится статическим,
  2. непостоянное поле становится нестатическим,
  3. класс становится интерфейсом,
  4. интерфейс стал класс
  5. если вы добавляете новое поле в класс / интерфейс (или добавляете новый супер-класс / супер-интерфейс), то статическое поле из супер-интерфейса клиентского класса C может скрывать добавленное поле (с тем же именем), унаследованное от супер-класса C (очень редко случай.)

Примечание: есть много другие исключения вызвано другими несовместимыми изменениями:NoSuchFieldError, NoSuchMethodError, IllegalAccessError, InstantiationError, исключение verifyerror, ошибке типа noclassdeffounderror и AbstractMethodError.

лучшая бумага о BC - " развитие Java-API 2: достижение Бинарная совместимость API" написал Джим де Ривьер.

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

использование japi-compliance-checker для вашего библиотека:

japi-compliance-checker OLD.jar NEW.jar

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

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

удачи!


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

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

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

разрешение дубликатов банок с Maven лучше всего делать с помощью комбинации в Maven-зависимость-плагин и maven-enforcer-плагин под Maven (или SBT Плагин Графика Зависимостей, затем добавьте эти банки в раздел вашего POM верхнего уровня или как импортированные элементы зависимостей в SBT (чтобы удалить эти зависимости).

хорошее удачи!


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

пример кода JNI C++:

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...
        }
    }
}

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

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


другая ситуация, когда эта ошибка может появиться с покрытием кода Emma.

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

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

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


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

public interface A{
}

public class AImpl implements A{
}

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

public abstract class A{
}

public class AImpl extends A{
}

после остановки и перезапуска домена все получилось отлично. Я использовал glassfish 3.1.43


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


мой ответ, я считаю, будет Intellij конкретным.

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

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


в моем случае, я столкнулся с этой ошибкой сюда. 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 на A и B's poms, чтобы избежать конфликта классов (у нас есть повторяющееся предупреждение класса) при построении окончательный проект, который зависит от обоих A и B.


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


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

Я только что получил эту ошибку на был (8.5.0.1), во время работы CXF (2.6.0) нагрузки пружины (3.1.1_release) конфигурация, в которой 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

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

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

  • Заявлений -> Типа -> Приложения Приложения WebSphere
  • нажмите ссылку, представляющую ваше приложение (война)
  • нажмите "Управление модулями" в разделе "модули"
  • нажмите на ссылку для базового модуль(ы)
  • изменить " порядок загрузчика классов "на"(родительский последний)".

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

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

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


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


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

Это похоже на ответ из Ogre Psalm33.

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 поэтому я хотел документировать этот особый случай.


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


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

исправления

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

  1. основные настройки Android Studio
  2. Сборка, Выполнение, Развертывание
  3. Мгновенный Запуск
  4. Untick " включить мгновенный запуск..."

почему

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