Code (which compiles):
for (Method m : ImmutableList.class.getMethods()) {
System.out.println(m);
}
ImmutableList.copyOf(Arrays.asList(new PlayerLevel[0]));
Output (annotated and shortened):
public final void com.google.common.collect.ImmutableList.add(int,java.lang.Object)
----> public static com.google.common.collect.ImmutableList com.google.common.collect.ImmutableList.copyOf(java.lang.Iterable)
public static com.google.common.collect.ImmutableList com.google.common.collect.ImmutableList.copyOf(java.util.Iterator)
(lots of other methods)
java.lang.NoSuchMethodError: com.google.common.collect.ImmutableList.copyOf(Ljava/util/Collection;)Lcom/google/common/collect/ImmutableList;
Huh?
(If the logs are not clear enough, I get an error saying that ImmutableList.copyOf(List)
is not a method, but by looping through all the methods I see there is a copyOf(Iterable)
, and List implements Iterable
.)
Both methods are compatible at compile time. But runtime is another beast. I assume, that your code compiles against an older version of Google Collections but runs against a newer version.
Edit: What happens in detail:
Given the lines
List<String tmpArray = Arrays.asList(new PlayerLevel[0]);
ImmutableList.copyOf(tmpArray);
the compiler starts to look for a suitable method in ImmutableList
with the name copyOf
and one parameter compatible to the static type List<String>
. The version of the class visible to the compiler offers exactly one match:
ImmutableList.copyOf(Collection<T> arg0);
Please note, that the compiler is not interested in the actual type of tmpArray
, only the static type (aka. "formal type") is considered.
The compiler writes the signature of the selected method into the class file.
At runtime the classloader / linker reads the class, finds the signature of the method
ImmutableList.copyOf(Collection<T> arg0);
and performs a lookup (not a search!) on ImmutableList
for exactly the given signature. Compatibility does not matter here, that was the job of the compiler. You get the same results, if you use reflection like this:
ImmutableList.class.method("copyOf", Collection.class);
In both cases Java simply performs a lookup using exactly the given type. It does not perform a search like "return method(s) which can be called with the given type".
In your case the runtime classpath and the compile time class are different. So the classloader / linker fails to perform the lookup.
One step back
This issue shows the different levels of compatibility:
You can use these keywords to look around this site or on Google for more infos. A good reference for binary compatibility are the three parts of Evolving Java-based APIs.