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
the compiler starts to look for a suitable method in
ImmutableListwith the namecopyOfand one parameter compatible to the static typeList<String>. The version of the class visible to the compiler offers exactly one match: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
and performs a lookup (not a search!) on
ImmutableListfor 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: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.