Je compresse une bibliothèque Java en tant que JAR et jette de nombreux Java.lang.IncompatibleClassChangeError
lorsque j'essaie d'appeler des méthodes à partir de celle-ci. Ces erreurs semblent apparaître au hasard. Quels types de problèmes pourraient être à l'origine de cette erreur?
Cela signifie que vous avez apporté des modifications binaires incompatibles à la bibliothèque sans recompiler le code client. Spécification du langage Java, §1 détaille tous ces changements, notamment en remplaçant les champs/méthodes non -static
par des champs/méthodes non privés par static
ou inversement.
Recompilez le code client par rapport à la nouvelle bibliothèque et vous devriez être prêt à partir.
UPDATE: Si vous publiez une bibliothèque publique, évitez autant que possible de faire des modifications binaires incompatibles afin de préserver ce que l'on appelle la "compatibilité ascendante binaire". La mise à jour des fichiers de dépendance uniquement ne devrait idéalement pas détruire l'application ou la construction. Si vous devez rompre la compatibilité ascendante binaire, il est recommandé d'augmenter le numéro de version majeure (par exemple de 1.x.y à 2.0.0) avant de publier la modification.
Votre bibliothèque nouvellement empaquetée n'est pas compatible avec les versions antérieures (BC) avec l'ancienne version. Pour cette raison, certains des clients de la bibliothèque qui ne sont pas recompilés peuvent renvoyer l'exception.
Il s'agit d'une liste complète des modifications apportées à l'API de bibliothèque Java pouvant entraîner l'envoi par Java de clients construits avec une ancienne version de la bibliothèque. .lang. IncompatibleClassChangeError s’ils s’exécutent sur une nouvelle (c.-à-d. en coupant le BC):
Remarque : Il existe de nombreuses autres exceptions causées par d'autres modifications incompatibles: NoSuchFieldError , NoSuchMethodError , IllegalAccessError , InstantiationError , VerifyError , NoClassDefFoundError et AbstractMethodError .
Le meilleur article sur la Colombie-Britannique est "Évolution des API basées sur Java 2: Obtenir la compatibilité binaire des API" écrit par Jim des Rivières.
Il existe également des outils automatiques permettant de 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, la résolution du problème est souvent plus difficile. C'est généralement le résultat de deux versions légèrement différentes de la même dépendance vis-à-vis du chemin d'accès aux classes, et est presque toujours causé par une superclasse différente de celle qui a été compilée à l'origine par rapport au fait d'être sur le chemin d'accès aux classes ou une certaine importation de la fermeture transitive étant différente, mais généralement à l’instanciation de classe et à l’invocation du constructeur. (Après un chargement de classe et une invocation de ctor réussis, vous obtiendrez NoSuchMethodException
ou autre chose.)
Si le comportement semble aléatoire, il est probable que le résultat d'un programme multithread chargé différentes dépendances transitives en fonction du code qui a été touché en premier.
Pour résoudre ces problèmes, essayez de lancer le VM avec -verbose
en tant qu'argument, puis examinez les classes en cours de chargement lorsque l'exception se produit. Vous devriez voir des informations surprenantes. Par exemple, avoir plusieurs copies de la même dépendance et des versions inattendues ou que vous auriez acceptées si vous saviez qu'elles étaient incluses.
La meilleure solution consiste à combiner maven-dependency-plugin et maven -forcer-plugin sous Maven (ou SBT's Dependency Graph Plugin) , puis en ajoutant ces fichiers JAR à une section de votre POM de niveau supérieur ou en tant qu’éléments de dépendance importés dans SBT (pour supprimer ces dépendances).
Bonne chance!
J'ai également découvert que, lors de l'utilisation de JNI, l'appel d'une méthode Java à partir de C++, si vous transmettez des paramètres à la méthode invoquée Java dans le mauvais ordre, vous obtiendrez cette erreur lorsque essayez d'utiliser les paramètres dans la méthode appelée (car ils ne seront pas du bon type). JNI ne vérifie pas cette tâche pour vous dans le cadre de la vérification de la signature de classe lorsque vous appelez la méthode, mais je suppose qu’ils ne font pas ce type de vérification car vous passez peut-être des paramètres polymorphes et ils doivent supposez que vous savez ce que vous faites.
Exemple de code 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.
}
Exemple Java Code:
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'avais le même problème et plus tard, j'ai découvert que j'exécutais l'application sur Java version 1.4 alors que l'application était compilée sur la version 6.
En fait, la raison en était d'avoir une bibliothèque en double, une est située dans le classpath et l'autre est incluse dans un fichier jar situé dans le classpath.
Dans mon cas, l'erreur est apparue lorsque j'ai ajouté la bibliothèque com.nimbusds
dans mon application déployée sur Websphere 8.5
.
L’exception ci-dessous s’est produite:
Causée par: Java.lang.IncompatibleClassChangeError: org.objectweb.asm.AnnotationVisitor
La solution consistait à exclure le fichier asm de la bibliothèque:
<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>
Je pense que ma réponse sera spécifique à Intellij.
J'avais reconstruit proprement, allant même jusqu'à supprimer manuellement les répertoires "out" et "target". Intellij a un "invalider les caches et redémarrer", ce qui efface parfois les erreurs impaires. Cette fois, ça n'a pas marché. Les versions de dépendance semblaient toutes correctes dans le menu Paramètres du projet-> Modules.
La réponse finale a été de supprimer manuellement la dépendance à mon problème de mon dépôt Maven local. Le coupable était une ancienne version de Bouncycastle (je savais que je venais de changer de version et que ce serait le problème) et bien que l'ancienne version ne montre aucun endroit dans ce qui était construit, cela résolvait mon problème. J'utilisais intellij version 14 et ensuite mis à niveau à 15 au cours de ce processus.
Une autre situation où cette erreur peut apparaître concerne Emma Code Coverage.
Cela se produit lors de l'affectation d'un objet à une interface. Je suppose que cela a quelque chose à voir avec l’objet en cours d’instrument et non plus compatible binaire.
http://sourceforge.net/tracker/?func=detail&aid=3178921&group_id=177969&atid=883351
Heureusement, ce problème ne se produit pas avec Cobertura, j'ai donc ajouté le plugin cobertura-maven dans les plugins de création de rapports de mon fichier pom.xml.
J'ai fait face à ce problème lors du déploiement et du redéploiement d'une guerre avec Glassfish. Ma structure de classe était comme ça,
public interface A{
}
public class AImpl implements A{
}
et il a été changé pour
public abstract class A{
}
public class AImpl extends A{
}
Après avoir arrêté et redémarré le domaine, cela a bien fonctionné. 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, lorsque je l'ai mis dans l'environnement qa (Tomcat - 8.0.20), il m'a continué à me donner IncompatibleClassChangeError et il se plaignait de mon extension sur une interface. Cette interface a été changée en une classe abstraite. Et j'ai compilé les classes pour parents et pour enfants et j'ai toujours eu le même problème. Enfin, je voulais déboguer, alors j'ai changé la version du parent en x.0.1-SNAPSHOT, puis tout compilé et maintenant, cela fonctionne. Si quelqu'un rencontre toujours le problème après avoir suivi les réponses données ici, assurez-vous que les versions de votre fichier pom.xml sont également correctes. Changez les versions pour voir si cela fonctionne. Si tel est le cas, corrigez le problème de version.
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 A
et B
dépendent tous les deux du même artefact (appelez-le C
), mais de versions différentes (C.1
et C.2
). Lorsque cela se produit, pour chaque classe de C
, maven ne peut sélectionner qu'une version de la classe parmi les deux versions (lors de la construction d'un ber-jar ). Il sélectionnera la version "la plus proche" en fonction de ses règles médiation de dépendance et émettra un avertissement "Nous avons une classe dupliquée ..." Si une signature de méthode/classe change entre les versions, cela peut provoquer une exception Java.lang.IncompatibleClassChangeError
si la version incorrecte est utilisée au moment de l'exécution.
Avancé: Si A
doit utiliser v1 de C
et B
doit utiliser v2 de C
, alors nous devons relocaliserC
in Les poms de A
et B
pour éviter les conflits de classes (nous avons un avertissement de classe dupliqué) lors de la construction du projet final qui dépend à la fois de A
et B
.
Ajout de mes 2 centimes. Si vous utilisez scala et sbt et scala-logging comme dépendance, cela peut se produire car la version précédente de scala-logging s'appelait scala-logging-api.So; essentiellement les résolutions de dépendance ne se produisent pas à cause de noms différents entraînant des erreurs d'exécution lors du lancement de l'application scala.
Pour une raison quelconque, la même exception est également générée lors de l'utilisation de JNI et lors de la transmission de l'argument jclass à la place de jobject lors de l'appel d'un Call*Method()
.
Ceci est similaire à la réponse de 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;
}
Je sais qu'il est un peu tard pour répondre à cette question cinq ans après avoir été interrogé, mais c'est l'un des grands succès de la recherche de Java.lang.IncompatibleClassChangeError
alors je voulais documenter ce cas particulier.
S'il s'agit d'un enregistrement d'occurrences possibles de cette erreur, alors:
Je viens de recevoir cette erreur sur WAS (8.5.0.1), lors du chargement CXF (2.6.0) de la configuration du ressort (3.1.1_release), où une exception BeanInstantiationException a démarré une exception CXF ExtensionException, ce qui a entraîné l'incompatibleClassChangeError. L'extrait suivant montre le Gist 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 consistait à modifier l'ordre des classpath du module dans mon fichier war. En d’autres termes, ouvrez l’application war dans la console WAS sous et sélectionnez le ou les modules client. Dans la configuration du module, définissez le chargement de classe sur "parent en dernier".
Cela se trouve dans la console WAS:
Veuillez vérifier si votre code ne consiste pas en deux projets de module ayant les mêmes noms de classes et définition de packages. Par exemple, cela peut arriver si quelqu'un utilise un copier-coller pour créer une nouvelle implémentation d'interface basée sur une implémentation précédente.
Tout ce qui précède - pour une raison quelconque, je faisais un grand refactor et commençais à l'obtenir. J'ai renommé le paquet dans lequel mon interface se trouvait et cela l'a effacé. J'espère que ça t'as aidé.
Une autre cause de ce problème est si vous avez activé Instant Run
pour Android Studio.
Si vous constatez que vous commencez à avoir cette erreur, désactivez Instant Run
.
Instant Run
modifie un grand nombre de choses au cours du développement, afin de rendre plus rapide la fourniture de mises à jour pour votre application en cours d'exécution. Par conséquent instantané. Quand ça marche, c'est vraiment utile. Cependant, lorsqu'un problème comme celui-ci se présente, la meilleure chose à faire est de désactiver Instant Run
jusqu'à la prochaine version de Android Studio.
Documenter un autre scénario après avoir brûlé beaucoup trop de temps.
Assurez-vous de ne pas avoir de fichier de dépendance comportant une classe avec une annotation EJB.
Nous avions un fichier JAR commun ayant une annotation @local
. Cette classe a ensuite été déplacée de ce projet commun vers notre projet principal ejb jar. Notre pot ejb et notre pot commun sont tous deux regroupés dans une oreille. La version de notre dépendance jar commune n'a pas été mise à jour. Ainsi, 2 classes essaient d'être quelque chose avec des modifications incompatibles.