为什么在这种情况下,单参数实例方法对 BiConsumer 的类型推断不同?

Why is the type inference to a BiConsumer for a one-argument instance method different in this case?

我正在尝试编译这段代码:

import java.util.Collection;
import java.util.function.BiConsumer;
import de.hybris.platform.servicelayer.exceptions.ModelSavingException;
import de.hybris.platform.servicelayer.model.ModelService;

public class Foo {

    public static interface ModelService2 {
        public abstract void saveAll(Object[] paramArrayOfObject) throws ModelSavingException;
        public abstract void saveAll(Collection<? extends Object> paramCollection) throws ModelSavingException;
        public abstract void saveAll() throws ModelSavingException;
    }

    public void bar() {
        final BiConsumer<ModelService2, Collection<? extends Object>> consumer1 = ModelService2::saveAll;
        final BiConsumer<ModelService, Collection<? extends Object>> consumer2 = ModelService::saveAll;
    }
}

接口ModelService由SAP hybris平台定义。 ModelService2 只是复制了在 hybris 平台的接口中定义的名称为 saveAll 的重载方法。

编译上面的代码时出现以下编译器错误:

1. ERROR in src\Foo.java (at line 17)
    final BiConsumer<ModelService, Collection<? extends Object>> consumer2 = ModelService::saveAll;
                                                                             ^^^^^^^^^^^^^^^^^^^^^
Cannot make a static reference to the non-static method saveAll(Object[]) from the type ModelService

为什么编译器对 ModelService 进行不同的类型推断,而我能够发现的唯一区别是每个接口所在的位置?

在这种情况下,我使用 javac 1.8.0_77 进行编译。例如,Eclipse 不会报告上述代码的任何错误。

编辑:

相对类似的错误也发生在以下变量声明中:

final Consumer<ModelService2> consumer3 = ModelService2::saveAll;
final Consumer<ModelService> consumer4 = ModelService::saveAll;

本例编译错误为:

1. ERROR in src\Foo.java (at line 19)
    final Consumer<ModelService> consumer4 = ModelService::saveAll;
                                             ^^^^^^^^^^^^^^^^^^^^^
Cannot make a static reference to the non-static method saveAll(Object[]) from the type ModelService

EDIT2:

编译参数是:

'-noExit'
'-classpath'
'<classpath>'
'-sourcepath'
'<source path>'
'-d'
'<path>\classes'
'-encoding'
'UTF8'

编辑 3:
这些是 Eclipse class 文件查看器显示的 3 种方法的定义:

  // Method descriptor #43 (Ljava/util/Collection;)V
  // Signature: (Ljava/util/Collection<+Ljava/lang/Object;>;)V
  public abstract void saveAll(java.util.Collection arg0) throws de.hybris.platform.servicelayer.exceptions.ModelSavingException;

  // Method descriptor #45 ([Ljava/lang/Object;)V
  public abstract void saveAll(java.lang.Object... arg0) throws de.hybris.platform.servicelayer.exceptions.ModelSavingException;

  // Method descriptor #10 ()V
  public abstract void saveAll() throws de.hybris.platform.servicelayer.exceptions.ModelSavingException;

解析:

问题是由 java v4.4.1 的 eclipse 编译器引起的。它至少从 v4.5.1 开始是固定的。一开始命令行编译的时候没注意到是hybris平台编译代码用的eclipse编译器

方法重载、可变参数和类型推断之间的交互可能是 Java 类型检查中最复杂和最棘手的部分。这是一个错误经常出现的领域,不同编译器之间经常存在差异。

我的猜测如下:

ModelService 有一个可变参数 saveAll。因为带有两个对象参数的 saveAll 是对此类对象的有效方法调用。如果该方法是静态的,则用一个 ModelService 和一个 Collection 调用它是有效的,因此方法引用表达式对 BiConsumer<ModelService2, Collection<? extends Object>> 类型有效。

由于编译器错误,编译器注意到该方法不是静态的,因此推断方法引用表达式在这里无效。这会产生编译错误。

另一方面,

ModelService2.saveAll 不是可变参数,不能用一个 ModelService 和一个 Collection 调用。因此,编译器在尝试这种可能性时不会陷入此错误。

当我尝试使用 Eclipse 4.5.2 和 javac 1.8 编写此代码时。0_77 您为我编译的所有示例。我不知道为什么你会得到不同的结果。