从Lambda调用的方法无法访问字段?

时间:2015-01-07 03:50:05

标签: lambda java-8

我一直在尝试使用lambdas并遇到了一个我不明白的问题。以下代码可以单独工作,但是在多线程环境(我无法控制)中以某种方式运行它会导致run()在一个实例中并且doA()在不同的实例中,我可以在调试器中看到不同的实例ID。

public class Main {
    private String string;

    public static void main(String[] args) {
        Main main = new Main();
        Main main2 = new Main();
        main2.run();

    }

    private interface Stepper {
        boolean execute();
    }

    Stepper[] steps = { () -> { return doA(); }, () -> { return doB(); }, () -> { return doC(); } };

    public boolean doA() { System.out.println(string); return true; }
    public boolean doB() { System.out.println(string); string = "foo"; return true; }
    public boolean doC() { System.out.println(string); return true; }

    private void run() {
        string = "changed";
        for (Stepper step : steps) {
            if (step.execute() == false) {
                return;
            }
        }
    }
}

真实类实现Runnable的专有后代,此处未显示。我将steps的初始化移到了run()方法中,它开始正常工作。如何使用run()方法之外的数组导致这个?

1 个答案:

答案 0 :(得分:1)

如果您更改了Stepper界面,则可以执行以下操作:

public class Main {
    private String string;

    public static void main(String[] args) {
        Main main = new Main();
        Main main2 = new Main();
        main2.run();

    }

    private interface Stepper extends Function<Main, Boolean> {
        default boolean execute(Main main){
            return this.apply(main);
        }
    }

    Stepper[] steps = {Main::doA, Main::doB, Main::doC};

    public boolean doA() { System.out.println(string); return true; }
    public boolean doB() { System.out.println(string); string = "foo"; return true; }
    public boolean doC() { System.out.println(string); return true; }

    private void run() {
        string = "changed";
        for (Stepper step : steps) {
            if (step.execute(this) == false) {
                return;
            }
        }
    }
}

这是因为方法引用是静态的,但是将this传递给execute函数可以确保当前实例始终是将被执行的实例。

如果你有一个多线程环境,那么没有什么能阻止另一个线程同时运行和交错来自这个实例的输出。我建议您将输出收集到StringBuilder并将其打印为一个操作,以确保输出不会交错。如果订单不重要那么公平,但它确保调试更容易知道每组输出来自同一个线程。您甚至可以考虑将线程ID附加到输出行。