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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With