Lambda Java参考


73

我想将转换anonymous classlambda expression。但是这个匿名类我使用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)

所以我的问题是:有没有办法引用“ lambda对象” this

Answers:


110

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

问题是您this在该main()方法中使用。主要方法是静态的,并且没有引用表示的对象this

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

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

更新:

Java语言规范15.27.2说

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

在lambda表达式的主体中(无论是隐式的还是隐式的)透明性(即与周围环境相同)将为实现提供更大的灵活性,并防止主体中不合格名称的含义相互依赖在超载解决方案上。

实际上,lambda表达式需要谈论自己(以递归方式调用其自身或调用其其他方法)是不寻常的,而使用名称来引用封闭类中的东西则更为常见。否则将被遮盖(这是toString())。如果有必要让lambda表达式引用自身(如通过this),则应改用方法引用或匿名内部类。


@gontard我不知道java8,但是您可以尝试代替它进行observable final传递吗?observablethis
BackSlash 2014年

@BackSlash是的,我知道我可以绕过此SSCCE中的此错误。但是我正在寻找关于lambda的更一般的解决方案或解释。
gontard 2014年

感谢您指出JLS的摘录,这正是我想要的。
gontard 2014年

@BackSlash您的意思是这样写吗?那将如何工作? final Observable observable = new Observable(); observable.addObserver(notifier -> { notifier.removeObserver(observable); });
Vikash

1
我很困惑,因为它看起来更新部分国家的东西完全相反比它上面的部分...

1

解决方法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
Dean Xu

1
@DeanXu不起作用。lambda表达式无法访问接口的getThis()方法,就像您的方法一样,就像它无法访问this实现接口的类的实例一样。
Holger
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.