java.lang.IncompatibleClassChangeErrorの原因は何ですか?


218

JavaライブラリをJARとしてパッケージ化していますが、そこjava.lang.IncompatibleClassChangeErrorからメソッドを呼び出そうとすると、多くのがスローされます。これらのエラーはランダムに表示されるようです。どのような問題がこのエラーを引き起こしている可能性がありますか?


Apache FOP 1.0とBarcode4JをテストしていたEclipseプロジェクトでは、Barcode4Jに付属する追加のライブラリが、FOPに付属するライブラリ(バージョン番号がより高いものもある)をオーバーライドしているようです。ビルドパス/クラスパスに何を入れるかを非常に注意する必要がある場合です。
Wivani

回答:


170

これは、クライアントコードを再コンパイルせずに、ライブラリに互換性のないバイナリ変更を加えたことを意味します。 Java言語仕様§13は、そのようなすべての変更の詳細を示しています。最も目立つのは、static非プライベートではないフィールド/メソッドの変更、staticまたはその逆です。

新しいライブラリに対してクライアントコードを再コンパイルすれば、問題ありません。

更新:パブリックライブラリを公開する場合は、「バイナリの下位互換性」と呼ばれるものを維持するために、互換性のないバイナリ変更をできるだけ行わないようにする必要があります。依存関係のあるjarファイルだけを更新しても、アプリケーションやビルドが壊れないことが理想的です。バイナリの下位互換性を解除する必要がある場合は、変更をリリースする前に、メジャーバージョン番号(1.xyから2.0.0など)を増やすことをお勧めします。


2
何らかの理由で、ここの開発者はクライアントコードを再コンパイルしても問題が正確に修正されないという問題を抱えています。何らかの理由で、発生したファイルを編集してエラーを再コンパイルすると、そこでエラーは発生しなくなりますが、ライブラリが参照されているプロジェクトの他の場所でランダムにポップアップ表示されます。何が原因なのか知りたいです。
ゾンビ

5
クリーンビルド(すべての*.classファイルを削除)して再コンパイルしようとしましたか?ファイルの編集にも同様の効果があります。
notnoop 2009

動的に生成されるコードはありません。JSPがそうであると見なさない限り。私たちはクラスファイルを削除しようとしましたが、これは役に立ちませんでした。奇妙なことに、それは私には起こらないようですが、他の開発者にも起こります。
ゾンビ

2
クリーンビルドを実行するときに、実行時に使用したものと同じjarに対してコンパイルしていることを確認できますか?
notnoop 2009

32ビットまたは64ビットプラットフォームに関する問題はありますか?エラーは64ビットプラットフォームでのみ実行されます。
カウボイペン2017

96

新しくパッケージ化されたライブラリ、旧バージョンとの下位バイナリ互換(BC)ではありません。このため、再コンパイルされていないライブラリクライアントの一部が例外をスローする場合があります。

これは、古いバージョンのライブラリで構築されたクライアントがjava.langをスローする原因となるJavaライブラリAPIの変更の完全なリストです。新しいもので実行する場合(つまり、BCを壊す場合)のIncompatibleClassChangeError

  1. 非最終フィールドは静的になります、
  2. 非定数フィールドは非静的になります、
  3. クラスはインターフェースになり、
  4. インターフェイスがクラスになり、
  5. クラス/インターフェースに新しいフィールドを追加した場合(または新しいスーパークラス/スーパーインターフェースを追加した場合)、クライアントクラスCのスーパーインターフェースの静的フィールドは、から継承された(同じ名前の)追加フィールドを非表示にする場合がありますCのスーパークラス(非常にまれなケース)。

:他の互換性のない変更によって引き起こされる他の多くの例外があります:NoSuchFieldErrorNoSuchMethodErrorIllegalAccessErrorInstantiationErrorVerifyErrorNoClassDefFoundError、およびAbstractMethodError

BCに関するより優れた論文は、Jim desRivièresによって書かれた「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

幸運を!


59

これらの答えはすべて正解ですが、問題の解決はしばしばより困難です。これは通常、クラスパスへの同じ依存関係の2つのわずかに異なるバージョンの結果であり、ほとんどの場合、クラスパス上にあることに対して元々コンパイルされたスーパークラスとは異なるスーパークラス、または推移的クロージャのインポートが異なるが、通常はクラスで発生します。インスタンス化とコンストラクターの呼び出し。(クラスのロードとctorの呼び出しが正常に完了すると、何がNoSuchMethodException起こるかがわかります。)

動作がランダムに見える場合は、マルチスレッドプログラムが最初にヒットしたコードに基づいてさまざまな推移的な依存関係をクラスロードした結果である可能性があります。

これらを解決するに-verboseは、引数としてVMを起動してから、例外が発生したときにロードされていたクラスを確認します。驚くべき情報が表示されるはずです。たとえば、同じ依存関係とバージョンの複数のコピーがあり、予期していなかった、または含まれていることがわかっていれば受け入れられなかったとします。

Mavenで重複したjarを解決するには、Mavenの下のmaven-dependency-pluginmaven-enforcer-plugin(またはSBTのDependency Graph Plugin)を組み合わせて、それらのjarをトップレベルのPOMのセクションに追加するか、インポートされた依存関係として追加するのが最適です。 SBTの要素(これらの依存関係を削除するため)。

幸運を!


5
詳細な議論は、どのjarが問題を引き起こしていたかを特定するのに役立ちました。ありがとう
Virat Kadaru 2013

6

JNIを使​​用してC ++からJavaメソッドを呼び出すときに、呼び出されたJavaメソッドに間違った順序でパラメーターを渡すと、呼び出されたメソッド内でパラメーターを使用しようとすると、このエラーが発生することも発見しました(正しいタイプではありません)。最初にJNIがメソッドの呼び出し時にクラスシグネチャチェックの一部としてこのチェックを行わないことに驚きましたが、ポリモーフィックパラメーターを渡す可能性があり、それらがあなたが何をしているか知っていると仮定します。

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

1
ヒントをありがとう。間違ったインスタンス(this)を指定してJavaメソッドを呼び出すと、同じ問題が発生しました。
sstn '19

5

同じ問題があり、後でバージョン6でアプリケーションがコンパイルされているときに、Javaバージョン1.4でアプリケーションを実行していることがわかりました。

実際には、ライブラリが重複していることが原因でした。1つはクラスパス内にあり、もう1つはクラスパス内にあるjarファイル内に含まれています。


どうやってこれの底に達したのですか?私は同様の問題があると確信していますが、どの依存関係ライブラリが複製されているかを追跡する方法の手がかりはありません。
beterthanlife 2013年

@beterthanlifeは、重複するクラスを探すすべてのjarファイル内を検索するスクリプトを記述します(完全修飾名、つまりパッケージ名で検索します):)
Eng.Fouad 2013年

わかりました。NetBeansとmaven-shadeを使用すると、複製されているすべてのクラスとそれらが存在するjarファイルを確認できます。これらのjarファイルの多くは、pom.xmlで直接参照していないので、私の依存関係に含まれている。各依存関係のpomファイルを経由せずに、どの依存関係にそれらが含まれているのかを見つける簡単な方法はありますか?(私のノビッシュをご容赦ください、Im a java virgin!)
beterthanlife 2013年

@beterthanlifeそれに関して新しい質問をするほうがいいです:)
Eng.Fouad

Gradleの依存関係のグラフ(./gradlew:<name>:dependencies)を調べて、重複したjarを見つけました。それで、古いバージョンのlibをプロジェクトに引き込んだ犯人を見つけました。
nyx

1

このエラーが発生する可能性があるもう1つの状況は、Emma Code Coverageです。

これは、オブジェクトをインターフェイスに割り当てるときに発生します。これは、インストゥルメントされているオブジェクトと関係があり、バイナリ互換ではなくなっていると思います。

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

幸い、この問題はCoberturaでは発生しないため、pom.xmlのレポートプラグインにcobertura-maven-pluginを追加しました


1

Glassfishとの戦争の配備解除および再配備中にこの問題に直面しました。私のクラス構造はこのようなものでした、

public interface A{
}

public class AImpl implements A{
}

そしてそれはに変更されました

public abstract class A{
}

public class AImpl extends A{
}

ドメインを停止して再起動した後、問題なく動作しました。Glassfish 3.1.43を使用していました


1

ローカルマシンのtomcat(8.0.20)に完全に問題なく展開するWebアプリケーションがあります。しかし、それをqa環境(tomcat-8.0.20)に配置すると、引き続きIncompatibleClassChangeErrorが返され、インターフェイスで拡張していると文句を言われていました。このインターフェースは抽象クラスに変更されました。そして、親クラスと子クラスをコンパイルしましたが、それでも同じ問題が発生し続けました。最後に、デバッグしたかったので、親のバージョンをx.0.1-SNAPSHOTに変更し、すべてをコンパイルして、現在は機能しています。ここでの回答に従っても問題が発生する場合は、pom.xmlのバージョンも正しいことを確認してください。バージョンを変更して、機能するかどうかを確認します。その場合は、バージョンの問題を修正してください。


1

私の答えは、Intellij固有のものだと思います。

私は、 "out"および "target"ディレクトリを手動で削除するまで、クリーンを再構築しました。Intellijには「キャッシュを無効にして再起動する」機能があり、奇妙なエラーがクリアされることがあります。今回はうまくいきませんでした。依存関係のバージョンはすべて、プロジェクト設定->モジュールメニューで正しく見えました。

最後の答えは、ローカルのMavenリポジトリから問題の依存関係を手動で削除することでした。bouncycastleの古いバージョンが原因でした(バージョンを変更したばかりで、それが問題であることがわかっていました)。古いバージョンは何が構築されているのかわからなかったものの、私の問題は解決しました。私はintellijバージョン14を使用していて、このプロセス中に15にアップグレードしました。


1

私の場合、この方法でこのエラーに遭遇しました。pom.xmlプロジェクトの2つの依存関係Aとを定義しましたB。そして、両方ともAB同じアーティファクト(それを呼び出すC)への依存関係を定義しましたが、その異なるバージョン(C.1およびC.2)です。これが発生した場合、Cmavenの各クラスは、2つのバージョンから1つのバージョンのクラスのみを選択できます(uber-jarのビルド)。依存関係メディエーションルールに基づいて「最も近い」バージョンを選択し、「クラスが重複しています...」という警告を出力しますメソッド/クラスシグネチャがバージョン間で変更されたjava.lang.IncompatibleClassChangeError場合、不適切なバージョンが実行時に使用されます。

Advanced:のAv1 CとのBv2を使用する必要がある場合はC、とのpomを再配置して CAおよびにB依存する最終プロジェクトをビルドするときに、クラスの競合(クラスの重複警告)を回避する必要AありBます。


1

私の場合、に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>

0

コードが、同じクラス名とパッケージ定義を持つ2つのモジュールプロジェクトで構成されていないかどうかを確認してください。たとえば、誰かがコピーと貼り付けを使用して、以前の実装に基づいてインターフェースの新しい実装を作成した場合、これが発生する可能性があります。


0

これがこのエラーの考えられる発生の記録である場合:

BeanInstantiationExceptionがCXF ExtensionExceptionをロールアップし、IncompatibleClassChangeErrorをロールアップするスプリング(3.1.1_release)構成のCXF(2.6.0)ロード中に、WAS(8.5.0.1)でこのエラーが発生しました。次のスニペットは、スタックトレースの要点を示しています。

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ファイルでモジュールのクラスパスの順序を変更することでした。つまり、WASコンソールでwarアプリケーションを開き、クライアントモジュールを選択します。モジュール構成で、クラスローディングを「親の最後」に設定します。

これはWASコンソールにあります。

  • アプリケーション->アプリケーションタイプ-> WebSphere Enterprise Applications
  • アプリケーション(戦争)を表すリンクをクリックします
  • 「モジュール」セクションの「モジュールの管理」をクリックします
  • 基礎となるモジュールのリンクをクリックします
  • 「クラスローダーの順序」を「(親が最後)」に変更します。

0

時間をかけすぎた後の別のシナリオの文書化。

EJBアノテーションが付いたクラスを持つ依存関係jarがないことを確認してください。

@local注釈が付いた共通のjarファイルがありました。そのクラスは後でその共通プロジェクトから、メインのejb jarプロジェクトに移動されました。私たちのejb jarと私たちの共通jarはどちらも耳の中にバンドルされています。共通のjar依存関係のバージョンは更新されませんでした。したがって、2つのクラスは互換性のない変更を加えたものにしようとしています。


0

上記のすべて-何らかの理由で、私はいくつかの大きなリファクタリングを行っていて、これを取得し始めました。私のインターフェースが含まれていたパッケージの名前を変更したところ、それがクリアされました。お役に立てば幸いです。


0

何らかの理由で、JNIを使​​用し、を呼び出すときにjobjectの代わりにjclass引数を渡すと、同じ例外がスローされ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年後にこの質問に答えるのは少し遅いことはわかっていますが、これは検索時のトップヒットの1つなjava.lang.IncompatibleClassChangeErrorので、この特別なケースを文書化したいと思いました。


0

私の2セントを追加します。依存関係としてscalaとsbtおよびscala-loggingを使用している場合、これはscala-loggingの以前のバージョンの名前がscala-logging-apiだったために発生する可能性があります。したがって、本質的に依存関係の解決は、 scalaアプリケーションの起動中にランタイムエラーを引き起こす名前。


0

この問題の別の原因はInstant Run、Android Studio を有効にしている場合です。

修正

このエラーが発生し始めたら、オフにしInstant Runます。

  1. Android Studioのメイン設定
  2. ビルド、実行、展開
  3. 即時実行
  4. 「インスタントランを有効にする...」のチェックを外します

なぜ

Instant Run実行中のアプリの更新をすばやく提供するために、開発中に多数の変更を行います。したがって、即時実行。それが機能するとき、それは本当に便利です。ただし、このような問題が発生した場合はInstant Run、Android Studioの次のバージョンがリリースされるまでオフにすることをお勧めします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.