views:

112

answers:

3

I have the following class in a common jar:

public class Common 
{
  public Common(List list)
  {
    ...  
  }  
}

I then change the constructor parameter from a List to a Collection as follows:

public class Common 
{
  public Common(Collection collection)
  {
    ...
  }
}

Rebuilding the common jar and running the system causes a NoSuchMethodError in any dependent class when it invokes the constructor until I recompile that class.

I've got a few ideas what's causing this, along the lines of how the constructor is bound in the bytecode of the dependent class, but I'm not 100% sure.

Please can somebody shed some light onto what's going on here?

Update

I've subsequently done a quick test and taken a look at the bytecode:

Compiled from "Client.java"
public class Client extends java.lang.Object{
public Client();
  Code:
   0:   aload_0
   1:   invokespecial #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   new #2; //class ArrayList
   3:   dup
   4:   invokespecial #3; //Method java/util/ArrayList."<init>":()V
   7:   astore_1
   8:   new #4; //class Common
   11:  dup
   12:  aload_1
   13:  invokespecial #5; //Method Common."<init>":(Ljava/util/List;)V
   16:  pop
   17:  return

}

Like Tom said, and as you can see on line 13, the exact constructor is bound at compile time.

You learn something new every day :-)

A: 

Are you importing the correct List and Collection classes? i.e. java.util.List and java.util.Collection?

matt b
Yes, all the imports are correct. The broken class compiles with out requiring any change and works correctly thereafter.
Nick Holt
A: 

I think it could be a problem with the library version. Are you sure that there is not another version of the commons library somewhere else in the same context ?

Freddy
The classpath is correct and there's just a single copy of the common jar on it.
Nick Holt
+4  A: 

javac resolves exactly which method or constructor to call at compile time. This does not occur at link time. As the constructor signature has changed, the linking step cannot find the requested method and therefore throws an error. You can fix the error by providing to constructors - one which takes an Collection the other List. Later a constructor taking an Iterable could be added.

Note, generic types do not form part of the signature, so those can be changed whilst still keeping binary compatibility. Both parameter and return types form part of the signature for methods (covariant returns cause synthetic bridge methods to be created).

There is a nice big section in the JLS defining exactly what constitutes binary compatible changes.

Tom Hawtin - tackline
Thanks Tom, that's what I suspected. I've added the byte code from a quick test I've just done that shows (at line 13) the explicit call to the constructor that has the list parameter.
Nick Holt