SpaceTrucker SpaceTrucker - 7 months ago 43
Java Question

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

I'm trying to compile this piece of code:

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;
}
}


The interface
ModelService
is defined by the SAP hybris platform.
ModelService2
just replicates the overloaded methods with name
saveAll
defined in the interface of the hybris platform.

I get the following compiler error when compiling the above:

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


Why does the compiler do different type inference for
ModelService
when the only difference I'm able to spot is where each of the interfaces is located?

I'm using javac 1.8.0_77 for compilation in this case. Eclipse for example doesn't report any errors for the above code.

EDIT:

A relatively similiar error happens also for the following variable declarations:

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


The compile error in this case is:

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:

compilation arguments are:

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

Lii Lii
Answer

The interaction between method overloading, varargs and type inference is perhaps the most complicated and hairy part of Java type checking. It's an area where bugs turn up regularly and where there are often differences between different compilers.

My guess is the following:

ModelService has a vararg saveAll. Because of this saveAll with two object argument is a valid method call to such an object. If that method would be static it would be valid to call it with one ModelService and one Collection, so a method reference expression would be valid for a BiConsumer<ModelService2, Collection<? extends Object>> type.

Because of a compiler bug the compiler notes that, and notes that the method in not static, and thus infers that the method reference expression is not valid here. This generates the compilation error.

ModelService2.saveAll is on the other hand is not a vararg and can not be called with one ModelService and one Collection. Because of this the compiler does not get stuck in this bug when it tries that possibility.

When I tried this code with Eclipse 4.5.2 and javac 1.8.0_77 all of your examples compiled for me. I have no idea why you are getting different results.

Comments