Quelles sont les causes de java.lang.IncompatibleClassChangeError?
j'empaquette une bibliothèque Java comme un pot, et il jette beaucoup de java.lang.IncompatibleClassChangeError
s quand j'essaie d'invoquer des méthodes à partir de lui. Ces erreurs apparaissent au hasard. Quels types de problèmes peuvent être à l'origine de cette erreur?
17 réponses
cela signifie que vous avez apporté des modifications binaires incompatibles à la bibliothèque sans recompiler le code client. Java Language Specification §13 les détails de tous ces changements, le plus en évidence, en changeant non static
non-privés champs/méthodes pour être static
ou vice versa.
recompiler le code client contre la nouvelle bibliothèque, et vous devriez être bon à aller.
mise à jour: si vous publiez une bibliothèque publique, vous devez éviter de faire des changements binaires incompatibles autant que possible pour préserver ce qui est connu sous le nom de "compatibilité binaire en arrière". La mise à jour des conteneurs de dépendances seule ne devrait idéalement pas briser l'application ou la construction. Si vous devez casser la compatibilité binaire en arrière, il est recommandé pour augmenter le nombre de version principale (par exemple à partir de 1.x.y à 2.0.0) avant de lancer la modification.
votre bibliothèque nouvellement empaquetée n'est pas compatible binaire rétrograde (BC) avec l'ancienne version. Pour cette raison, certains des clients de la bibliothèque qui ne sont pas recompilés peuvent jeter l'exception.
Ceci est une complète liste des changements dans L'API de bibliothèque Java qui peuvent amener les clients construits avec une ancienne version de la bibliothèque à jeter java.lang. IncompatibleClassChangeError si ils s'exécutent sur un nouveau (c'est à dire breaking BC):
- Non définitive champ de devenir statique,
- Non à champ constant devenir non-statique,
- Class devenir l'interface, le
- de l'Interface de devenir la classe,
- si vous ajoutez un nouveau champ À class / interface (ou ajoutez une nouvelle super-class/super-interface) alors un champ statique à partir d'une super-interface d'une classe client C peut cacher un champ ajouté (avec le même nom) hérité de la super-classe de C (cas très rare).
Note : il existe de nombreuses autres exceptions causées par d'autres changements incompatibles: NoSuchFieldError , NoSuchMethodError , IllegalAccessError , instantiationerror , 1519290920 "Verifyerror , 1519290920" noclassdeffonderror et Abstracttmethoderror .
Le meilleur papier sur la colombie-est "l'Évolution de Java Api 2: la Réalisation de l'API de la Compatibilité Binaire" écrit par Jim des Rivières.
il y a aussi quelques outils automatiques pour détecter de tels changements:
utilisation de japi-compliance-checker pour votre bibliothèque:
japi-compliance-checker OLD.jar NEW.jar
utilisation de l'outil clirr:
java -jar clirr-core-0.6-uber.jar -o OLD.jar -n NEW.jar
bonne chance!
bien que toutes ces réponses soient correctes, il est souvent plus difficile de résoudre le problème. Il est généralement le résultat de deux versions légèrement différentes de la même dépendance sur le classpath, et est presque toujours causé par une superclasse différente de celle compilée à l'origine contre être sur le classpath ou quelque importation de la fermeture transitive étant différente, mais généralement à l'instanciation de classe et à l'invocation du constructeur. (Après la réussite de la classe chargement et invocation de ctor, vous obtiendrez NoSuchMethodException
ou autre.)
si le comportement semble aléatoire, c'est probablement le résultat d'un programme multithread chargeant différentes dépendances transitives basées sur ce code qui a été frappé en premier.
pour résoudre ces problèmes, essayez de lancer la VM avec -verbose
comme argument, puis regardez les classes qui étaient chargées lorsque l'exception se produit. Vous devriez voir des informations surprenantes. Par exemple, d'avoir plusieurs copies de la même dépendance et versions vous n'avez jamais prévu ou aurait accepté si vous saviez qu'ils étaient inclus.
résoudre les pots en double avec Maven est mieux fait avec une combinaison de la maven-plugin de dépendance et maven-enforcer-plugin sous Maven (ou SBT plugin de graphique de dépendance , puis l'ajout de ces pots à une section de votre POM de haut niveau ou comme importé éléments de dépendances dans SBT (pour supprimer ces dépendances).
bonne chance!
j'ai également découvert que, lorsque vous utilisez JNI, en invoquant une méthode Java à partir de C++, si vous passez les paramètres à la méthode Java invoquée dans le mauvais ordre, vous obtiendrez cette erreur lorsque vous tenterez d'utiliser les paramètres à l'intérieur de la méthode appelée (parce qu'ils ne seront pas le bon type). J'ai d'abord été surpris que JNI ne fasse pas ce genre de vérification pour vous dans le cadre de la vérification de la signature de classe lorsque vous invoquez la méthode, mais je suppose qu'ils ne font pas ce genre de vérification parce que vous pouvez être passer des paramètres polymorphes et ils doivent supposer que vous savez ce que vous faites.
exemple C++ Code 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.
}
Exemple De Code 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...
}
}
}
j'ai eu le même problème, et plus tard j'ai compris que j'exécutais L'application sur Java version 1.4 alors que l'application est compilée sur la version 6.
en fait, la raison était d'avoir une bibliothèque dupliquée, l'une se trouve dans classpath et l'autre est incluse dans un fichier jar qui se trouve dans classpath.
une autre situation où cette erreur peut apparaître est la couverture du code Emma.
cela se produit lors de l'assignation d'un objet à une interface. Je suppose que cela a quelque chose à voir avec L'objet étant instrumenté et pas compatible binaire plus.
http://sourceforge.net/tracker/?func=detail&aid=3178921&group_id=177969&atid=883351
heureusement ce problème ne se produit pas avec Cobertura, donc J'ai ajouté cobertura-maven-plugin dans mes plugins de reportage de mon pom.xml
j'ai fait face à ce problème tout en défaisant et en redéployant une guerre avec glassfish. Ma structure de classe était comme ceci,
public interface A{
}
public class AImpl implements A{
}
et il a été changé en
public abstract class A{
}
public class AImpl extends A{
}
après l'arrêt et le redémarrage du domaine, tout s'est bien passé. J'utilisais glassfish 3.1.43
j'ai une application web qui se déploie parfaitement sur tomcat de ma machine locale(8.0.20). Cependant, quand je l'ai mis dans l'environnement qa (tomcat - 8.0.20), il a continué à me donner L'Error Incompatibleclasschangeer et il se plaignait que je m'étendais sur une interface. Cette interface a été changée en classe abstraite. Et j'ai compilé les cours pour parents et enfants et j'ai continué à avoir le même problème. Enfin, je voulais déboguer, donc, j'ai changé la version sur le parent pour x.0.1-SNAPSHOT puis compilé tout et maintenant il fonctionne. Si quelqu'un est toujours frapper le problème après avoir suivi les réponses données ici, assurez-vous que les versions dans votre pom.xml sont également corrects. Changez les versions pour voir si cela fonctionne. Si c'est le cas, corrigez le problème de version.
ma réponse, je crois, sera Intellij spécifique.
j'ai reconstruit propre, allant même jusqu'à supprimer manuellement les "out" et "cible" dirs. Intellij a un "invalidate caches and restart", qui efface parfois les erreurs impaires. Cette fois, il ne fonctionne pas. Les versions de dépendances étaient toutes correctes dans le menu project settings->modules.
la réponse finale était de supprimer manuellement ma dépendance de problème de mon Maven repo local. Vieux version de bouncycastle était le coupable (je savais que je venais de changer les versions et ce serait le problème) et bien que l'ancienne version n'a pas montré où dans ce qui était construit, il a résolu mon problème. J'utilisais la version 14 d'intellij et je suis passé à 15 au cours de ce processus.
dans mon cas, j'ai rencontré cette erreur de cette façon. pom.xml
de mon projet a défini deux dépendances A
et B
. Et à la fois A
et B
définit la dépendance sur le même artefact (appelez-le C
) mais différentes versions de celui-ci ( C.1
et C.2
). Lorsque cela se produit, pour chaque classe de C
maven ne peut sélectionner qu'une seule version de la classe parmi les deux versions (tout en construisant un uber-jar ). Il sélectionnez la version" la plus proche "en fonction de ses règles médiation de dépendances et affichera un avertissement " nous avons une classe dupliquée..." si une signature de méthode/Classe change entre les versions, elle peut causer une exception java.lang.IncompatibleClassChangeError
si la version incorrecte est utilisée à l'exécution.
avancé: si A
doit utiliser v1 de C
et B
doit utiliser v2 de C
, alors nous devons relocaliser C
dans A
et B
poms pour éviter les conflits de classe (nous avons une double classe d'avertissement) lors de la construction du projet final qui dépend à la fois A
et B
.
s'il vous plaît vérifier si votre code ne se compose pas de deux projets de module qui ont les mêmes noms de classes et la définition de paquets. Par exemple, cela peut se produire si quelqu'un utilise copy-paste pour créer une nouvelle implémentation d'interface basée sur une implémentation précédente.
S'il s'agit d'un enregistrement d'occurrences possibles de cette erreur alors:
je viens d'obtenir cette erreur sur WAS (8.5.0.1), lors du chargement CXF (2.6.0) du ressort (3.1.1_release) configuration dans laquelle une exception de BeanInstantiationException a regroupé une exception D'ExtensionException CXF, ce qui a entraîné un changement de classe incompatible. L'extrait suivant montre l'essentiel de la trace de la pile:
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
dans ce cas, la solution était de changer l'ordre de le module dans mon dossier de guerre. C'est-à-dire, ouvrez l'application war dans la console WAS et sélectionnez le(S) module (s) client (s). Dans la configuration du module, définissez la charge de classe comme étant "parent last".
se trouve dans la console WAS:
- Applicatoins - > Types D'Application - > WebSphere Enterprise Applications
- cliquez sur le lien représentant votre application (war)
- cliquez sur "Gérer les Modules" sous Les "Modules" de la section
- Cliquez sur le lien pour le module sous-jacent(s)
- Changement "Class loader pour" être "(parent dernier)".
documentant un autre scénario après avoir brûlé beaucoup trop de temps.
assurez-vous que vous n'avez pas de pot de dépendances avec une classe avec une annotation EJB dessus.
nous avions un fichier jar commun qui avait une annotation @local
. Cette classe a été plus tard déplacée hors de ce projet commun et dans notre projet principal EJB jar. Notre pot ejb et notre pot commun sont tous deux emmitouflés dans une oreille. La version de notre dépendance jar commune n'a pas été mise à jour. Ainsi 2 classes essayent d'être quelque chose avec des changements incompatibles.
Tout ce qui précède - pour quelque raison que ce soit, je faisais un grand remaniement et je commençais à obtenir ceci. J'ai renommé le paquet dans lequel était mon interface et ça l'a effacé. Espérons que cela aide.
pour une raison quelconque, la même exception est également lancée lorsque vous utilisez JNI et que vous passez l'argument jclass au lieu de jobject lorsque vous appelez un Call*Method()
.
c'est semblable à la réponse du Psalm33 de L'Ogre.
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;
}
je sais qu'il est un peu tard pour répondre à cette question 5 ans après avoir été posé, mais c'est l'un des meilleurs coups sûrs lors de la recherche de java.lang.IncompatibleClassChangeError
donc je voulais documenter ce cas spécial.
ajoutant mes 2 cents .Si vous utilisez scala et sbt et scala-logging comme dépendance ,alors cela peut se produire parce que la version précédente de scala-logging avait le nom scala-logging-api.Ainsi, essentiellement les résolutions de dépendances ne se produisent pas en raison de noms différents conduisant à des erreurs d'exécution lors du lancement de l'application scala.
une autre cause de ce problème, est si vous avez activé Instant Run
pour Android Studio.
the fix
si vous constatez que vous commettez cette erreur, désactivez Instant Run
.
- Android Studio paramètres principaux
- Construction, Exécution, Déploiement
- Instant Run
- Décocher "Activer l'instant exécuter..."
pourquoi
Instant Run
modifie un grand nombre de choses pendant le développement, pour le rendre plus rapide à fournir des mises à jour à votre application en cours d'exécution. Par conséquent instant exécuter. Quand il fonctionne, il est vraiment utile. Cependant, quand un problème comme cette frappe, la meilleure chose à faire est de désactiver le Instant Run
jusqu'à la prochaine version d'Android Studio versions.