Here's sample code that fails to compile in Java 8 (1.8.0_40) but compiles in Eclipse 4.4 JDT standalone compiler (Bundle-Version: 3.10.0.v20140604-1726) and runs successfully:
import java.util.Arrays;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* Test method references to local class constructors.
*/
public class LocalClassTest {
public long sumOfLengths(String[] input) {
class Sum {
final long value;
Sum(long value) {
this.value = value;
}
Sum(Sum left, Sum right) {
this.value = left.value + right.value;
}
Sum add(String s) {
return new Sum(value + s.length());
}
}
return Arrays.stream(input)
.reduce(new Sum(0), Sum::add, Sum::new)
.value;
}
static String[] input =
{
"a", "ab", "abc"
};
@Test
public void localClassConstructorMethodRefs() {
assertEquals(sumOfLengths(input), 6);
}
}
javac
spits out the following error message:
Error:(32, 25) java: incompatible types: cannot infer type-variable(s) U
(argument mismatch; invalid constructor reference
cannot access constructor Sum(Sum,Sum)
an enclosing instance of type LocalClassTest is not in scope)
Reading through JLS 15.9.2 (Determining Enclosing Instances) and 15.13.3 (Run-Time Evaluation of Method References), I can't see why this should fail to compile, and the error message seems outright false since I can manually construct a Sum
instance in the same method just fine.
I've found a previous question Constructor reference for inner class fails with VerifyError at runtime that seems relevant, and the linked JDK issue there (https://bugs.openjdk.java.net/browse/JDK-8037404) indicate a series of compiler issues in this area; maybe this is another instance of a more general problem. Nevertheless, I'm not sure I'm reading the spec right; could this work in another way?
Java provides a new feature called method reference in Java 8. Method reference is used to refer method of functional interface. It is compact and easy form of lambda expression. Each time when you are using lambda expression to just referring a method, you can replace your lambda expression with method reference.
A method reference requires a target type similar to lambda expressions. But instead of providing an implementation of a method, they refer to a method of an existing class or object while a constructor reference provides a different name to different constructors within a class.
A method reference can also be applicable to constructors in Java 8. A constructor reference can be created using the class name and a new keyword. The constructor reference can be assigned to any functional interface reference that defines a method compatible with the constructor.
This does look like a compiler bug dealing with local classes (class declaration within a method). If you change to an inner class (move the class declaration outside the method), then it works fine. You could change it to be a static class at the same time, which would marginally reduce overhead.
(Inner classes and local class both have an implicit reference to the containing class, which means the constructor has a "hidden" first parameter, which you can see with javap. I suspect the compiler knows to compensate for this hidden parameter when using Sum::new
for an inner class, but the logic is just missing to do the same for a local class.)
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