Reputation: 182
There is a jar which has 2 versions in version 1 there are majorly 2 classes
Class A
Class B
Class A has a constructor as
A(X,B)
in version 2 there are majorly 2 classes and an interface
Class A
Interface C
Class B implements C
Class A has a constructor as
A(X,C)
In my util class I call the constructor of A
A a= new A(x,new B());
Above code is ant build using jar 1 which builds fine.
Now in production env the jar in classpath is of version 2. and I am getting error as
NoSuchMethodError A.<init>(x;B)
Not able to understand that in version 2 jar B is implemention of C so why this error? Please help me understand
Upvotes: 2
Views: 176
Reputation: 182
When the code was complied with version1 jar then class file was generated with constructor having argument as class file (class B) and it didnot had the interface C. When the same jar was moved to production env, constructor was having interface C (type C) which was not matching with class B (type B) hence the error of nosuchmethoderror.When code was complied with version 2 jar the error got resolved.
Upvotes: 0
Reputation: 14651
It is due to the fact that you are changing the function signature of a constructor . If you check the bytecode of your Util
class, you will see that it is using invokespecial
, not invokevirtual
opcode (constructors and private methods are called via invokespecial
). invokespecial
is really special in a sense that it does static binding. For a longer explanation I suggest reading this: article
Invokespecial differs from invokevirtual primarily in that invokespecial selects a method based on the type of the reference rather than the class of the object. In other words, it does static binding instead of dynamic binding. In each of the three situations where invokespecial is used, dynamic binding wouldn't yield the desired result.
By that version change you make after compilation, you are actually erasing the only constructor that your Util class can call, and you cannot rely on dynamic binding this time.
Upvotes: 2
Reputation: 41133
My bet is version 1 is still lurking somewhere in your production classpath alongside version 2 and it's the first one your classloader encounter.
In fact you should treat this as a blessing because otherwise you have a tiny silent bug which never cause havoc until some random time in the future when your classloader give you v1 (happens in my previous company and production goes into a grinding halt for a full week).
This is often difficult to debug because version 1 can be injected by another jar, war, by the container, transitively by maven, accidentally when packing the jar/war, osgi bundle, or worse: by a rogue classloader (good luck with this)
If it's taking you forever to solve this I'd rename A version 2 into A2 if I can so it has deterministically different name (yes this is an ugly solution).
Another common cause is JBoss not cleaning itself between multiple redeploys. So when you deploy app version 567, classpath still has junk from version 566. Typically this can be solved by doing a clean deploy (kill JBoss process, delete temp folder, start it again)
Upvotes: 1