JavaライブラリをJARとしてパッケージ化していますが、そこjava.lang.IncompatibleClassChangeError
からメソッドを呼び出そうとすると、多くのがスローされます。これらのエラーはランダムに表示されるようです。どのような問題がこのエラーを引き起こしている可能性がありますか?
JavaライブラリをJARとしてパッケージ化していますが、そこjava.lang.IncompatibleClassChangeError
からメソッドを呼び出そうとすると、多くのがスローされます。これらのエラーはランダムに表示されるようです。どのような問題がこのエラーを引き起こしている可能性がありますか?
回答:
これは、クライアントコードを再コンパイルせずに、ライブラリに互換性のないバイナリ変更を加えたことを意味します。 Java言語仕様§13は、そのようなすべての変更の詳細を示しています。最も目立つのは、static
非プライベートではないフィールド/メソッドの変更、static
またはその逆です。
新しいライブラリに対してクライアントコードを再コンパイルすれば、問題ありません。
更新:パブリックライブラリを公開する場合は、「バイナリの下位互換性」と呼ばれるものを維持するために、互換性のないバイナリ変更をできるだけ行わないようにする必要があります。依存関係のあるjarファイルだけを更新しても、アプリケーションやビルドが壊れないことが理想的です。バイナリの下位互換性を解除する必要がある場合は、変更をリリースする前に、メジャーバージョン番号(1.xyから2.0.0など)を増やすことをお勧めします。
*.class
ファイルを削除)して再コンパイルしようとしましたか?ファイルの編集にも同様の効果があります。
新しくパッケージ化されたライブラリは、旧バージョンとの下位バイナリ互換(BC)ではありません。このため、再コンパイルされていないライブラリクライアントの一部が例外をスローする場合があります。
これは、古いバージョンのライブラリで構築されたクライアントがjava.langをスローする原因となるJavaライブラリAPIの変更の完全なリストです。新しいもので実行する場合(つまり、BCを壊す場合)のIncompatibleClassChangeError:
注:他の互換性のない変更によって引き起こされる他の多くの例外があります:NoSuchFieldError、NoSuchMethodError、IllegalAccessError、InstantiationError、VerifyError、NoClassDefFoundError、および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
幸運を!
これらの答えはすべて正解ですが、問題の解決はしばしばより困難です。これは通常、クラスパスへの同じ依存関係の2つのわずかに異なるバージョンの結果であり、ほとんどの場合、クラスパス上にあることに対して元々コンパイルされたスーパークラスとは異なるスーパークラス、または推移的クロージャのインポートが異なるが、通常はクラスで発生します。インスタンス化とコンストラクターの呼び出し。(クラスのロードとctorの呼び出しが正常に完了すると、何がNoSuchMethodException
起こるかがわかります。)
動作がランダムに見える場合は、マルチスレッドプログラムが最初にヒットしたコードに基づいてさまざまな推移的な依存関係をクラスロードした結果である可能性があります。
これらを解決するに-verbose
は、引数としてVMを起動してから、例外が発生したときにロードされていたクラスを確認します。驚くべき情報が表示されるはずです。たとえば、同じ依存関係とバージョンの複数のコピーがあり、予期していなかった、または含まれていることがわかっていれば受け入れられなかったとします。
Mavenで重複したjarを解決するには、Mavenの下のmaven-dependency-pluginとmaven-enforcer-plugin(またはSBTのDependency Graph Plugin)を組み合わせて、それらのjarをトップレベルのPOMのセクションに追加するか、インポートされた依存関係として追加するのが最適です。 SBTの要素(これらの依存関係を削除するため)。
幸運を!
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...
}
}
}
同じ問題があり、後でバージョン6でアプリケーションがコンパイルされているときに、Javaバージョン1.4でアプリケーションを実行していることがわかりました。
実際には、ライブラリが重複していることが原因でした。1つはクラスパス内にあり、もう1つはクラスパス内にあるjarファイル内に含まれています。
このエラーが発生する可能性があるもう1つの状況は、Emma Code Coverageです。
これは、オブジェクトをインターフェイスに割り当てるときに発生します。これは、インストゥルメントされているオブジェクトと関係があり、バイナリ互換ではなくなっていると思います。
http://sourceforge.net/tracker/?func=detail&aid=3178921&group_id=177969&atid=883351
幸い、この問題はCoberturaでは発生しないため、pom.xmlのレポートプラグインにcobertura-maven-pluginを追加しました
ローカルマシンのtomcat(8.0.20)に完全に問題なく展開するWebアプリケーションがあります。しかし、それをqa環境(tomcat-8.0.20)に配置すると、引き続きIncompatibleClassChangeErrorが返され、インターフェイスで拡張していると文句を言われていました。このインターフェースは抽象クラスに変更されました。そして、親クラスと子クラスをコンパイルしましたが、それでも同じ問題が発生し続けました。最後に、デバッグしたかったので、親のバージョンをx.0.1-SNAPSHOTに変更し、すべてをコンパイルして、現在は機能しています。ここでの回答に従っても問題が発生する場合は、pom.xmlのバージョンも正しいことを確認してください。バージョンを変更して、機能するかどうかを確認します。その場合は、バージョンの問題を修正してください。
私の答えは、Intellij固有のものだと思います。
私は、 "out"および "target"ディレクトリを手動で削除するまで、クリーンを再構築しました。Intellijには「キャッシュを無効にして再起動する」機能があり、奇妙なエラーがクリアされることがあります。今回はうまくいきませんでした。依存関係のバージョンはすべて、プロジェクト設定->モジュールメニューで正しく見えました。
最後の答えは、ローカルのMavenリポジトリから問題の依存関係を手動で削除することでした。bouncycastleの古いバージョンが原因でした(バージョンを変更したばかりで、それが問題であることがわかっていました)。古いバージョンは何が構築されているのかわからなかったものの、私の問題は解決しました。私はintellijバージョン14を使用していて、このプロセス中に15にアップグレードしました。
私の場合、この方法でこのエラーに遭遇しました。pom.xml
プロジェクトの2つの依存関係A
とを定義しましたB
。そして、両方ともA
、B
同じアーティファクト(それを呼び出すC
)への依存関係を定義しましたが、その異なるバージョン(C.1
およびC.2
)です。これが発生した場合、C
mavenの各クラスは、2つのバージョンから1つのバージョンのクラスのみを選択できます(uber-jarのビルド中)。依存関係メディエーションルールに基づいて「最も近い」バージョンを選択し、「クラスが重複しています...」という警告を出力します。メソッド/クラスシグネチャがバージョン間で変更されたjava.lang.IncompatibleClassChangeError
場合、不適切なバージョンが実行時に使用されます。
Advanced:のA
v1 C
とのB
v2を使用する必要がある場合はC
、とのpomを再配置して C
、A
およびにB
依存する最終プロジェクトをビルドするときに、クラスの競合(クラスの重複警告)を回避する必要がA
ありB
ます。
私の場合、に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>
これがこのエラーの考えられる発生の記録である場合:
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コンソールにあります。
何らかの理由で、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
ので、この特別なケースを文書化したいと思いました。
この問題の別の原因はInstant Run
、Android Studio を有効にしている場合です。
このエラーが発生し始めたら、オフにしInstant Run
ます。
Instant Run
実行中のアプリの更新をすばやく提供するために、開発中に多数の変更を行います。したがって、即時実行。それが機能するとき、それは本当に便利です。ただし、このような問題が発生した場合はInstant Run
、Android Studioの次のバージョンがリリースされるまでオフにすることをお勧めします。