【问题标题】:How to reason about IntelliJ's hints about a Java 8 Lambda Expression?如何推断 IntelliJ 关于 Java 8 Lambda 表达式的提示?
【发布时间】:2016-06-02 14:48:34
【问题描述】:

考虑 Java 方法中的以下代码:

public void doWork() {
    Runnable runner1 = new Runnable() {
        @Override
        public void run() {
            System.out.println(toString());
        }
    };
    new Thread(runner1).start();
}

当我将鼠标悬停在 new Runnable 上时,IntelliJ 会执行以下操作:

也就是说,它让我可以选择将这个 sn-p 转换为 lambda 表达式。

当我将 run() 方法更改为引用 this 时,我放弃了此功能,但是:

我想我隐约了解发生了什么(lambda 表达式范围内变量的有效终结性),但想获得有关匿名内部类和 lambda 表达式可互换性的更全面的答案。

更新后Hank D's Answer

我同意您对 this 参考分配的评估,但是 让我们看看在 IntelliJ 认为匿名内部类可以被(等效的)lambda 表达式替换的情况下程序的行为。因此,我运行了这个方法(我的外部类的名字是LambdaScope):

public void doWork() {
    Runnable runner1 = new Runnable() {
        @Override
        public void run() {
            System.out.println(toString());
        }
    };
    new Thread(runner1).start();
}

它会产生以下输出(它显然来自这个匿名内部类从Object 继承的toString() 方法):

LambdaScope$1@7a88bb53

当我选择采用 IntelliJ 的建议并将匿名内部类转换为 lambda 表达式时,它变为:

public void doWork() {
    Runnable runner1 = () -> System.out.println(toString());
    new Thread(runner1).start();
}

运行此方法会产生以下结果(我已经覆盖了外部类中的方法toString()):

Outer class: class LambdaScope

我认为这是行为上的改变。那么这是一个错误吗?

这是完整的示例(您可以在 IDE 中打开它并尝试重新创建我的步骤以查看我的困惑):

public class LambdaScope {
    public void doWork() {
        Runnable runner1 = new Runnable() {
            @Override
            public void run() {
                System.out.println(toString());
            }
        };
        new Thread(runner1).start();
    }
    @Override
    public String toString() {
        return "Outer class: " + this.getClass();
    }

    public static void main(String[] args) {
        LambdaScope ls = new LambdaScope();
        ls.doWork();
    }
}

【问题讨论】:

  • 我用来自 JLS 的信息更新了我的答案,我希望能澄清它为什么这样做

标签: intellij-idea lambda java-8 anonymous-inner-class


【解决方案1】:

它可能会避开它,因为this 的含义在 lambda 和匿名类中是不同的。在您的匿名类中,this 指的是您正在创建的 Runnable 的实例,而在 lambda 中,this 将指的是 doWork 是其成员的对象。因为自动转换会改变行为,所以它不建议改变。

JLS 15.27.2 中,它说,

与出现在匿名类声明中的代码不同,名称的含义以及出现在 lambda 主体中的 this 和 super 关键字,以及引用声明的可访问性,与周围上下文中的相同

无论您在 this.toString() 中显式引用 this 还是在调用 toString() 时隐式引用,这都是正确的。

JLS 稍后会说,

实际上,lambda 表达式需要谈论自身(递归调用自身或调用其其他方法)是不寻常的,而更常见的是希望使用名称来引用封闭中的事物否则会被隐藏的类(this,toString())。如果 lambda 表达式需要引用自身(就像通过 this 一样),则应使用方法引用或匿名内部类。

所以,我希望您从 JSL 文档中看到,您看到的行为不是错误,IntelliJ 不提供转换是正确的,因为行为会改变。

【讨论】:

  • 更具体地说,不可能创建等效的 lambda 表达式,因为 lambda 表达式无法访问为其创建的 Runnable 实例。我感谢 IntelliJ 事先认识到这一点,而不仅仅是提供基于模式的转换和转换。
  • 汉克,我喜欢你的回答,但我还不能接受,因为我认为这是 IntelliJ 中的一个错误;)。
  • @KedarMhaswade:HankD 的回答完全正确地描述了为什么它不建议对第二种情况进行转换。您是说第一种情况是错误。这与 HankD 回答的任何部分都不矛盾。
  • 我希望传达这一点,虽然我现在明白“为什么它不建议对第二种情况进行转换”,但尚不清楚它在第一种情况下提供的建议如何正确。我还不相信 IDE 进行的转换(在选择让它转换时)是等效的(因为它产生不同的输出)。
  • 我可以在 Eclipse 中看到类似的行为:如果找到显式 this,则拒绝转换,但忽略隐式 this。显然,对于隐式情况,两种工具的分析都不完整。如果您将toString() 更改为this.toString(),至少Eclipse 会拒绝转换。我假设 IntelliJ 也会这样做。我只是想知道,如果考虑所有隐含的 this 引用将是一个实用的策略......
猜你喜欢
  • 1970-01-01
  • 2017-11-16
  • 1970-01-01
  • 2020-07-17
  • 1970-01-01
  • 1970-01-01
  • 2015-06-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多