JavaライブラリをJARとしてパッケージ化していますが、java.lang.IncompatibleClassChangeError
そこからメソッドを呼び出そうとすると、多くのsがスローされます。これらのエラーはランダムに表示されるようです。このエラーの原因はどのような問題ですか?
18 に答える
これは、クライアントコードを再コンパイルせずに、ライブラリに互換性のないバイナリ変更を加えたことを意味します。 Java言語仕様§13では、このようなすべての変更について詳しく説明しています。最も顕著なのは、static
非プライベートフィールド/メソッドを非公開フィールド/メソッドに変更すること、static
またはその逆に変更することです。
新しいライブラリに対してクライアントコードを再コンパイルすると、準備が整います。
更新:パブリックライブラリを公開する場合は、「バイナリの下位互換性」と呼ばれるものを維持するために、互換性のないバイナリの変更をできるだけ行わないようにする必要があります。依存関係のjarを単独で更新しても、アプリケーションやビルドが破損することはありません。バイナリの下位互換性を破る必要がある場合は、変更をリリースする前にメジャーバージョン番号を増やすことをお勧めします(たとえば、1.xyから2.0.0に)。
新しくパッケージ化されたライブラリは、古いバージョンとの下位バイナリ互換(BC)ではありません。このため、再コンパイルされない一部のライブラリクライアントは例外をスローする場合があります。
これは、古いバージョンのライブラリでビルドされたクライアントがjava.langをスローする原因となる可能性のあるJavaライブラリAPIの変更の完全なリストです。新しいもので実行された場合(つまり、BCを壊した場合)のIncompatibleClassChangeError :
- 非最終フィールドは静的になり、
- 非定数フィールドは非静的になり、
- クラスはインターフェースになり、
- インターフェイスはクラスになり、
- クラス/インターフェイスに新しいフィールドを追加する(または新しいスーパークラス/スーパーインターフェイスを追加する)と、クライアントクラスCのスーパーインターフェイスの静的フィールドが、から継承された(同じ名前の)追加フィールドを非表示にする場合があります。 Cのスーパークラス(非常にまれなケース)。
注:他の互換性のない変更によって引き起こされる他の多くの例外があります: NoSuchFieldError、NoSuchMethodError、IllegalAccessError、InstantiationError、VerifyError、NoClassDefFoundError、AbstractMethodError。
BCに関するより良い論文は、 JimdesRiviè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(またはSBTの依存関係グラフプラグイン)の下でmaven-dependency-pluginとmaven-enforcer-pluginを組み合わせて、それらのjarをトップレベルのPOMのセクションに追加するか、インポートされた依存関係として追加するのが最適です。 SBTの要素(これらの依存関係を削除するため)。
幸運を!
また、JNIを使用してC ++からJavaメソッドを呼び出すときに、呼び出されたJavaメソッドに間違った順序でパラメーターを渡すと、呼び出されたメソッド内でパラメーターを使用しようとすると、このエラーが発生することもわかりました(パラメーターが正しいタイプにはなりません)。最初は、メソッドを呼び出すときにJNIがクラス署名チェックの一部としてこのチェックを行わないことに驚いていましたが、多態的なパラメーターを渡す可能性があるため、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ファイル内に含まれています。
私の場合、にcom.nimbusds
デプロイされたアプリケーションにライブラリを追加すると、エラーが発生しましたWebsphere 8.5
。
以下の例外が発生しました:
原因:java.lang.IncompatibleClassChangeError:org.objectweb.asm.AnnotationVisitor
解決策は、asmjarをライブラリから除外することでした。
<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>
このエラーが発生する可能性があるもう1つの状況は、Emmaコードカバレッジです。
これは、オブジェクトをインターフェイスに割り当てるときに発生します。これは、インストルメントされているオブジェクトと関係があり、バイナリ互換ではなくなったと思います。
http://sourceforge.net/tracker/?func=detail&aid=3178921&group_id=177969&atid=883351
幸い、この問題はCoberturaでは発生しないため、pom.xmlのレポートプラグインにcobertura-maven-pluginを追加しました。
Glassfishとの戦争を展開解除および再展開しているときに、この問題に直面しました。私のクラス構成はこんな感じでした
public interface A{
}
public class AImpl implements A{
}
そしてそれはに変更されました
public abstract class A{
}
public class AImpl extends A{
}
ドメインを停止して再起動した後、正常に機能しました。Glassfish3.1.43を使用していました
ローカルマシンのtomcat(8.0.20)に完全に正常にデプロイされるWebアプリケーションがあります。ただし、qa環境(tomcat --8.0.20)に配置すると、IncompatibleClassChangeErrorが発生し続け、インターフェイスを拡張しているとの不満がありました。このインターフェースは抽象クラスに変更されました。そして、私は親と子のクラスをコンパイルしましたが、それでも同じ問題が発生し続けました。最後に、デバッグしたかったので、親のバージョンをx.0.1-SNAPSHOTに変更してから、すべてをコンパイルしました。これで動作します。ここに記載されている回答に従った後も問題が発生する場合は、pom.xmlのバージョンも正しいことを確認してください。バージョンを変更して、それが機能するかどうかを確認します。その場合は、バージョンの問題を修正してください。
私の答えは、Intellij固有のものになると思います。
「out」および「target」dirを手動で削除するところまで行っても、クリーンに再構築しました。Intellijには「キャッシュを無効にして再起動する」機能があり、奇妙なエラーをクリアすることがあります。今回はうまくいきませんでした。依存関係のバージョンはすべて、プロジェクト設定->モジュールメニューで正しく表示されました。
最終的な答えは、問題の依存関係をローカルのMavenリポジトリから手動で削除することでした。弾む城の古いバージョンが原因であり(バージョンを変更したばかりで、それが問題になることはわかっていました)、古いバージョンは構築中の場所に表示されませんでしたが、問題は解決しました。私はintellijバージョン14を使用していましたが、このプロセス中に15にアップグレードしました。
私の場合、この方法でこのエラーが発生しました。私のプロジェクトの2つの依存関係とをpom.xml
定義しました。そして、両方と定義された依存関係は、同じアーティファクト(と呼びます)ですが、バージョンが異なります(と)。これが発生すると、Mavenのクラスごとに、2つのバージョンからクラスの1つのバージョンのみを選択できます(uber-jarの構築中)。依存関係メディエーションルールに基づいて「最も近い」バージョンを選択し、「クラスが重複しています...」という警告を出力します。バージョン間でメソッド/クラスのシグネチャが変更された場合、誤ったバージョンが実行時に使用されます。A
B
A
B
C
C.1
C.2
C
java.lang.IncompatibleClassChangeError
高度:A
v1 ofC
をB
使用する必要があり、v2 ofを使用する必要がある場合は、との両方に依存する最終プロジェクトをビルドするときに、クラスの競合を回避するためにとのpomsに再配置C
する必要があります(重複クラスの警告があります) 。 C
A
B
A
B
コードが、同じクラス名とパッケージ定義を持つ2つのモジュールプロジェクトで構成されていないかどうかを確認してください。たとえば、これは、誰かがコピーアンドペーストを使用して、以前の実装に基づいてインターフェイスの新しい実装を作成した場合に発生する可能性があります。
これがこのエラーの発生の可能性の記録である場合は、次のようにします。
BeanInstantiationExceptionがCXFExtensionExceptionをロールアップし、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アプリケーションを開き、クライアントモジュールを選択します。モジュール構成で、class-loadingを「parentlast」に設定します。
これは、WASコンソールにあります。
- アプリケーション->アプリケーションタイプ->WebSphereEnterprise Applications
- アプリケーションを表すリンクをクリックします(戦争)
- 「モジュール」セクションの下の「モジュールの管理」をクリックします
- 基盤となるモジュールのリンクをクリックします
- 「クラスローダーの順序」を「(親の最後)」に変更します。
あまりにも多くの時間を費やした後、別のシナリオを文書化します。
EJBアノテーションが付いたクラスを持つ依存関係jarがないことを確認してください。
@local
注釈付きの一般的なjarファイルがありました。そのクラスは、後でその共通プロジェクトからメインのejbjarプロジェクトに移動されました。ejbjarとcommonjarはどちらも耳の中にバンドルされています。一般的なjar依存関係のバージョンは更新されませんでした。したがって、2つのクラスが互換性のない変更を加えようとしています。
上記のすべて-何らかの理由で、私はいくつかの大きなリファクタリングを行っていて、これを取得し始めていました。インターフェイスが含まれていたパッケージの名前を変更したところ、クリアされました。お役に立てば幸いです。
何らかの理由で、JNIを使用し、を呼び出すときにジョブジェクトの代わりにjclass引数を渡す場合にも、同じ例外がスローされCall*Method()
ます。
これは、OgrePsalm33からの回答に似ています。
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
この特殊なケースを文書化したいと思いました。
私の2セントを追加します。依存関係としてscalaとsbtおよびscala-loggingを使用している場合、これは、scala-loggingの以前のバージョンの名前がscala-logging-apiであったために発生する可能性があります。したがって、基本的に、依存関係の解決は異なるために発生しません。 Scalaアプリケーションの起動中にランタイムエラーを引き起こす名前。
この問題のもう1つの原因は、AndroidStudioをInstant Run
有効にしている場合です。
修正
このエラーが発生し始めた場合は、をオフにしてくださいInstant Run
。
- AndroidStudioのメイン設定
- ビルド、実行、デプロイ
- インスタントラン
- [インスタント実行を有効にする...]のチェックを外します
なぜ
Instant Run
開発中に多くのことを変更して、実行中のアプリに更新をすばやく提供できるようにします。したがって、インスタント実行。それが機能するとき、それは本当に便利です。Instant Run
ただし、このような問題が発生した場合は、 AndroidStudioの次のバージョンがリリースされるまでオフにするのが最善の方法です。