I'm packaging a Java library as a JAR, and it's throwing many java.lang.IncompatibleClassChangeError
s when I try to invoke methods from it. These errors seem to appear at random. What kinds of problems could be causing this error?
相关问题
- Delete Messages from a Topic in Apache Kafka
- Jackson Deserialization not calling deserialize on
- How to maintain order of key-value in DataFrame sa
- StackExchange API - Deserialize Date in JSON Respo
- Difference between Types.INTEGER and Types.NULL in
While these answers are all correct, resolving the problem is often more difficult. It's generally the result of two mildly different versions of the same dependency on the classpath, and is almost always caused by either a different superclass than was originally compiled against being on the classpath or some import of the transitive closure being different, but generally at class instantiation and constructor invocation. (After successful class loading and ctor invocation, you'll get
NoSuchMethodException
or whatnot.)If the behavior appears random, it's likely the result of a multithreaded program classloading different transitive dependencies based on what code got hit first.
To resolve these, try launching the VM with
-verbose
as an argument, then look at the classes that were being loaded when the exception occurs. You should see some surprising information. For instance, having multiple copies of the same dependency and versions you never expected or would have accepted if you knew they were being included.Resolving duplicate jars with Maven is best done with a combination of the maven-dependency-plugin and maven-enforcer-plugin under Maven (or SBT's Dependency Graph Plugin, then adding those jars to a section of your top-level POM or as imported dependency elements in SBT (to remove those dependencies).
Good luck!
I have also discovered that, when using JNI, invoking a Java method from C++, if you pass parameters to the invoked Java method in the wrong order, you will get this error when you attempt to use the parameters inside the called method (because they won't be the right type). I was initially taken aback that JNI does not do this checking for you as part of the class signature checking when you invoke the method, but I assume they don't do this kind of checking because you may be passing polymorphic parameters and they have to assume you know what you are doing.
Example C++ JNI Code:
Example Java Code:
Your newly packaged library is not backward binary compatible (BC) with old version. For this reason some of the library clients that are not recompiled may throw the exception.
This is a complete list of changes in Java library API that may cause clients built with an old version of the library to throw java.lang.IncompatibleClassChangeError if they run on a new one (i.e. breaking BC):
Note: There are many other exceptions caused by other incompatible changes: NoSuchFieldError, NoSuchMethodError, IllegalAccessError, InstantiationError, VerifyError, NoClassDefFoundError and AbstractMethodError.
The better paper about BC is "Evolving Java-based APIs 2: Achieving API Binary Compatibility" written by Jim des Rivières.
There are also some automatic tools to detect such changes:
Usage of japi-compliance-checker for your library:
Usage of clirr tool:
Good luck!
An additional cause of this issue, is if you have
Instant Run
enabled for Android Studio.The fix
If you find you start getting this error, turn off
Instant Run
.Why
Instant Run
modifies a large number of things during development, to make it quicker to provide updates to your running App. Hence instant run. When it works, it is really useful. However, when an issue such as this strikes, the best thing to do is to turn offInstant Run
until the next version of Android Studio releases.I have a web application that deploys perfectly fine on my local machine's tomcat(8.0.20). However, when I put it into the qa environment (tomcat - 8.0.20), it kept on giving me the IncompatibleClassChangeError and it was complaining that I was extending on an interface. This interface was changed to an abstract class. And I compiled the parent and child classes and still I kept on getting the same issue. Finally, I wanted to debug, so, I changed the version on the parent to x.0.1-SNAPSHOT and then compiled everything and now it is working. If someone is still hitting the problem after following the answers given here, please make sure the versions in your pom.xml are also correct. Change the versions to see if that works. If so, then fix the version problem.
This means that you have made some incompatible binary changes to the library without recompiling the client code. Java Language Specification §13 details all such changes, most prominently, changing non-
static
non-private fields/methods to bestatic
or vice versa.Recompile the client code against the new library, and you should be good to go.
UPDATE: If you publish a public library, you should avoid making incompatible binary changes as much as possible to preserve what's known as "binary backward compatibility". Updating dependency jars alone ideally shouldn't break the application or the build. If you do have to break binary backward compatibility, it's recommended to increase the major version number (e.g. from 1.x.y to 2.0.0) before releasing the change.