Java 8中的本地类构造函数的方法引用如何工作?

时间:2015-05-19 18:21:30

标签: java lambda java-8

这里的示例代码无法在Java 8(1.8.0_40)中编译,但在Eclipse 4.4 JDT独立编译器(Bundle-Version:3.10.0.v20140604-1726)中编译并成功运行:

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吐出以下错误消息:

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)

通过JLS 15.9.2(确定封闭实例)和15.13.3(方法引用的运行时评估)阅读,我无法理解为什么这应该无法编译,并且错误消息似乎完全错误,因为我可以用同样的方法手动构建一个Sum实例。

我发现之前的问题Constructor reference for inner class fails with VerifyError at runtime似乎相关,而那里的链接JDK问题(https://bugs.openjdk.java.net/browse/JDK-8037404)表明该领域存在一系列编译问题;也许这是一个更普遍的问题的另一个例子。不过,我不确定我是否正确阅读规范;这可以用另一种方式工作吗?

1 个答案:

答案 0 :(得分:4)

这看起来像处理本地类的编译器错误(方法中的类声明)。如果您更改为内部类(在方法外移动类声明),那么它可以正常工作。您可以同时将其更改为静态类,这可以略微降低开销。

(内部类和本地类都有一个对包含类的隐式引用,这意味着构造函数有一个“隐藏”的第一个参数,你可以用javap看到它。我怀疑编译器知道补偿这个隐藏参数时使用Sum::new作为内部类,但缺少对本地类执行相同操作的逻辑。)