【问题标题】:Why context is static in this Java 8 stream example?为什么在这个 Java 8 流示例中上下文是静态的?
【发布时间】:2016-04-17 05:48:29
【问题描述】:

在 Java 8 中有以下简单方法:

public void test(){

    Stream<Integer> stream = Stream.of(1,2,3);
    stream.map(Integer::toString);
}

我得到两个错误:

java:不兼容的类型:无法推断类型变量 R (参数不匹配;无效的方法引用

对 toString 的引用不明确 java.lang.Integer 中的方法 toString(int) 和方法 toString() 在 java.lang.Integer 中

和:

无效的方法引用非静态方法 toString() 不能 从静态上下文引用

第一个错误可以理解,Integer类有两种方法:

public static String toString(int i)
public String toString()

并且编译器无法推断出所需的方法引用。

但是关于第二个,编译器引用的静态上下文在哪里?

错误与非静态的 Integer 类的 toString() 方法有关,但为什么我使用 map() 调用该方法的上下文是静态的?

还有一个问题,如果编译器必须解决两个导致编译时错误的方法之间的歧义,他不应该选择另一个吗?

【问题讨论】:

  • 我不明白 - 你怎么得到第二个错误?收到第一个错误后,您是否会以某种方式修改代码?
  • 请注意,您的流并未终止。这样做可能会对编译器有所帮助。
  • 第二个错误可能只是未能解决第一个错误的副作用。
  • 引用是不明确的,因为这两种方法都是在此上下文中有效。另见here。正如@khelwood 正确指出的那样,第二个错误只是副作用。通常,javac 在错误消息方面很糟糕,尤其是对于 Java 8 功能。如果有疑问,请先尝试修复所有可理解的错误,然后再查看其他错误(如果修复后仍然出现)。你可以使用Object::toString;缩小toString() 的接收者类型从来没有优势。
  • 甚至已经接受了答案。我添加了更多解释。

标签: java java-8 java-stream


【解决方案1】:

第二个错误是红鲱鱼。它暴露了编译器的一些内部工作原理。问题是存在歧义问题,第二个问题是其结果,可以忽略。它可能在做的事情如下。

  1. 它检查是否存在与 “有效”签名。有,所以它假设静态是方式 去。这强烈暗示存在某种“偏好” 在静态方法的编译器中,虽然这可能是 随意。

  2. 然后它会去寻找第一个匹配签名的方法。 它不是静态的,所以它会感到困惑,因为它之前确实找到了 具有该签名的静态方法。

在混合的某个地方,它还发现引用是模棱两可的。目前尚不清楚第 1 步或第 2 步是否会发生这种情况,但编译器不会中止,因为它试图提供帮助并发现进一步的编译错误。

编译器理论上可以更好地处理这个问题,因为第二条消息令人困惑。

注意:Eclipse 编译器不显示第二个错误。

【讨论】:

  • 第二个错误未显示,因为 Eclipse 不使用javac 进行编译。它使用自己的编译器。
【解决方案2】:

为什么我们会出现两个错误的解释是方法引用Integer::toString可以作为参考

  • 到特定类型对象的实例方法
  • 到静态方法

以下 sn-ps 应该演示编译器在两种情况下为 Integer::toString 选择的内容。

将选择实例方法i.toString()

static class MyInteger {
    int value;
    public MyInteger(int i) {
        this.value = i;
    }
    public String toMyString() {
       return "instance " + value;
    }
}

Stream<MyInteger> stream = ...
stream.map(MyInteger::toMyString).forEach(System.out::println);
/* which would be equivalent to
   stream.map(new Function<MyInteger, String>() {
       public String apply(MyInteger t) {
           return t.toMyString();
       }
   });

   // as method argument for stream.map() the compiler generates
   invokevirtual MyInteger.toMyString:()Ljava/lang/String;
*/

将选择静态方法Integer.toString(i)

static class MyInteger {
    int value;
    public MyInteger(int i) {
        this.value = i;
    }
    public static String toMyString() {
       return "static " + value;
    }
}

Stream<MyInteger> stream = ...
stream.map(MyInteger::toMyString)..forEach(System.out::println);
/* which would be equivalent to
   stream.map(new Function<MyInteger, String>() {
       @Override
       public String apply(MyInteger t) {
           return MyInteger.toMyString(t);
       }
   });

   // as method argument for stream.map() the compiler generates
   invokestatic MyInteger.toMyString:(LMyInteger;)Ljava/lang/String;
*/

在第一遍中,解析器尝试找到可以在对象实例上调用的方法。因为toMyString() 和toMyString(MyInteger) 两种方法都可以在MyInteger 类型的对象上调用,并且都满足Function&lt;? super T,? extends R&gt; 的要求,所以我们得到第一个错误reference to toString is ambiguous。
(见源:com.sun.tools.javac.comp.Resolve.mostSpecific(...))。

在第二遍中,解析器试图找到一个静态方法toString。由于对(先前解决的)方法toString() 的引用不是静态的,我们得到第二个错误non-static method toString() cannot be referenced from a static context。
(见源:com.sun.tools.javac.comp.Resolve.resolveMemberReference(...))。

edit 一个小例子来解释这两个错误的原因。 javac 的解析器无法知道您在 Integer::toString 上的意图。您的意思可能是i.toString() 或Integer.toString(i),所以他对这两种情况都进行了验证。这也是他在其他情况下的工作方式。

举个例子:

class Foo {
    int x + y = 1;
}

报告的错误是

Scratch.java:2: error: ';' expected          
    int x + y = 1;
         ^                               
Scratch.java:2: error: <identifier> expected 
        int x + y = 1;
                 ^                           

在这个小 sn-p 中,解析器也不知道你的意图是什么。看到一些可能性。

int x; y = 1; // missed the semicolon and the declaration for `y`
int x = y = 1; // typo at `+`
int x = y + 1; // swapped `+` and `=` and missed declaration of `y`
... more possibilities exist

在这种情况下,解析器也不会在第一个错误之后立即停止。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-07-01
    • 1970-01-01
    • 2016-12-18
    • 2016-01-03
    • 2014-06-15
    • 2013-08-18
    • 1970-01-01
    相关资源
    最近更新 更多