存在的方法......不是吗?

Bar*_*lom 3 java classpath nosuchmethoderror guava

代码(编译):

    for (Method m : ImmutableList.class.getMethods()) {
        System.out.println(m);
    }

    ImmutableList.copyOf(Arrays.asList(new PlayerLevel[0]));
Run Code Online (Sandbox Code Playgroud)

输出(注释和缩写):

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;
Run Code Online (Sandbox Code Playgroud)

咦?

(如果日志不够清楚,我得到一个错误,说这ImmutableList.copyOf(List)不是一个方法,但通过循环遍历所有方法,我看到有一个copyOf(Iterable),和List implements Iterable.)

A.H*_*.H. 6

两种方法在编译时兼容.但运行时是另一种野兽.我假设您的代码针对较旧版本的Google Collections进行编译,但针对较新版本运行.

编辑:详细情况:

鉴于线条

List<String tmpArray = Arrays.asList(new PlayerLevel[0]);
ImmutableList.copyOf(tmpArray);
Run Code Online (Sandbox Code Playgroud)

编译器开始ImmutableList使用名称copyOf和一个与静态类型兼容的参数来查找合适的方法List<String>.编译器可见的类的版本只提供一个匹配:

ImmutableList.copyOf(Collection<T> arg0);
Run Code Online (Sandbox Code Playgroud)

请注意,编译器对实际类型感兴趣,只考虑静态类型(又称"正式类型").tmpArray

编译器将所选方法的签名写入类文件.

在运行时,类加载器/链接器读取类,找到方法的签名

ImmutableList.copyOf(Collection<T> arg0);
Run Code Online (Sandbox Code Playgroud)

并且对于给定的签名执行查找(而不是搜索!)ImmutableList.兼容性在这里无关紧要,这是编译器的工作.如果你使用这样的反射,你会得到相同的结果:

ImmutableList.class.method("copyOf", Collection.class);
Run Code Online (Sandbox Code Playgroud)

在这两种情况下,Java只使用完全给定的类型执行查找.它不执行类似"可以使用给定类型调用的返回方法"的搜索.

在您的情况下,运行时类路径和编译时类是不同的.因此类加载器/链接器无法执行查找.

退一步

此问题显示了不同级别的兼容性:

  • 二进制兼容性:扔进一个新罐子就是这样.
  • 源兼容性:您必须编译源代码,但不必更改它.
  • 行为兼容性语义兼容性:必须更改客户端代码.

您可以使用这些关键字浏览此网站或Google以获取更多信息.二进制兼容性的一个很好的参考是Evolving基于Java的API的三个部分.