【问题标题】:Lambda this reference in java在java中Lambda这个参考
【发布时间】:2014-08-03 19:56:51
【问题描述】:

我想将匿名类转换为 lambda 表达式。但是这个匿名类使用了this 关键字。

例如,我写了这个简单的 Observer/Observable 模式:

import java.util.ArrayList;
import java.util.Collection;

public static class Observable {
    private final Collection<Observer> notifiables = new ArrayList<>();

    public Observable() { }

    public void addObserver(Observer notifiable) { notifiables.add(notifiable); }
    public void removeObserver(Observer notifiable) { notifiables.add(notifiable); }

    public void change() {
        notifiables.forEach(notifiable -> notifiable.changed(this));
    }
}

public interface Observer {
    void changed(Observable notifier);
}

以及这个带有匿名类的示例代码(使用 this 关键字):

public class Main {

    public static void main(String[] args) {
        Observable observable = new Observable();
        observable.addObserver(new Observer() {
            @Override
            public void changed(Observable notifier) {
                notifier.removeObserver(this);
            }
        });
        observable.change();
    }
}

但是当我将其转换为 lambda 表达式时:

public class Main {

    public static void main(String[] args) {
        Observable observable = new Observable();
        observable.addObserver(notifier -> { notifier.removeObserver(this); });
        observable.change();
    }
}

我得到这个编译错误:

Cannot use this in a static context and in a non `static` context



public class Main {
    public void main(String[] args) {
        method();
    }

    private void method() {
        Observable observable = new Observable();
        observable.addObserver(notifier -> {
                notifier.removeObserver(this);
        });
        observable.change();
    }
}

编译错误是:

The method removeObserver(Main.Observer) in the type Main.Observable is not applicable for the arguments (Main)

有没有办法用this 引用 lambda 对象?

【问题讨论】:

    标签: java lambda java-8


    【解决方案1】:

    您不能在 lambda 表达式中引用 thisthis 的语义已更改为仅从 lambda 中引用周围类的实例。无法从 lambda 内部引用 lambda 表达式的 this

    问题是你在main() 方法中使用了this。 main 方法是静态的,没有引用代表this 的对象。

    当您在内部类的实例中使用this 时,您正在引用内部类的实例。 lambda 表达式不是内部类,this 没有引用 lambda 表达式的实例。它引用您在其中定义 lambda 表达式的类的实例。在您的情况下,它将是 Main 的实例。但是由于您使用的是静态方法,因此没有实例。

    这是您的第二个编译错误告诉您的内容。您将 Main 的一个实例移交给您的方法。但是你的方法签名需要一个 Observer 的实例。

    更新:

    Java Language Specification 15.27.2 says:

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

    this 在 lambda 表达式主体中的透明性(显式和隐式)——也就是说,将其与周围上下文中的处理相同——允许实现更大的灵活性,并防止正文中未限定名称的含义不依赖于重载解析。

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

    【讨论】:

    • @gontard 我不知道 java8,但你能不能尝试制作 observable final 并传递 observable 而不是 this
    • @BackSlash 是的,我知道我可以在这个 SSCCE 中绕过这个错误。但我正在寻找关于 lambdas 的更通用的解决方案或解释。
    • 感谢您指出 JLS 的摘录,这正是我想要的。
    • @BackSlash 你的意思是像下面这样写吗?那将如何工作? final Observable observable = new Observable(); observable.addObserver(notifier -&gt; { notifier.removeObserver(observable); });
    • 我很困惑,因为它看起来更新部分与上面的部分完全相反......
    【解决方案2】:

    解决方法 1

    您的change() 方法无论如何都会抛出ConcurrentModificationException

    public class Main {
        public static void main(String[] args) {
            Observable observable = new Observable();
            final Observer[] a = new Observer[1];
            final Observer o = er -> er.removeObserver(a[0]); // !!
            a[0] = o;
            observable.addObserver(o);
            observable.change();
        }
    }
    public class Observable {
        private final java.util.Collection<Observer> n
            = java.util.new ArrayList<>();
        public void addObserver(Observer notifiable) {
            n.add(notifiable);
        }
        public void removeObserver(Observer notifiable) {
            n.add(notifiable);
        }
        public void change() {
            for (final Observer o : n.toArray(new Observer[n.size()])) {
                o.changed(this);
            }
        }
    }
    public interface Observer {
        void changed(Observable notifier);
    }
    

    解决方法 2

    我将changed(Observable) 更改为changed(Observable, Observer),以便观察者可以自行处理。

    public class Main {
        public static void main(String[] args) {
            Observable observable = new Observable();
            final Observer o = (er, ee) -> er.removeObserver(ee); // !!
            observable.addObserver(o);
            observable.change();
        }
    }
    public class Observable {
        private final java.util.Collection<Observer> n
            = new java.util.ArrayList<>();
        public void addObserver(Observer notifiable) {
            n.add(notifiable);
        }
        public void removeObserver(Observer notifiable) {
            n.add(notifiable);
        }
        public void change() {
            for (final Observer o : n.toArray(new Observer[n.size()])) {
                o.changed(this, o);
            }
        }
    }
    public interface Observer {
        void changed(Observable notifier, Observer notifiee);
    }
    

    【讨论】:

    • 我有另一个解决方法。您可以在观察者中添加default Observer getThis(){return this;}。并使用getThis() 而不是this
    • @DeanXu 不起作用。 lambda 表达式不能访问接口的方法,就像你的 getThis() 方法一样,就像它不能访问实现接口的类的 this 实例一样。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-25
    相关资源
    最近更新 更多