Java Runnable 作为参数评估

Java Runnable as argument evaluation

我正在 java 中开发客户端服务器,我遇到了将自定义 Runnable 实现作为参数从一个对象传递到另一个对象的问题。 问题是 Runnable 代码在定义时被评估(未执行),但我希望它在调用时被评估。 有什么办法可以实现这种行为吗?

受此问题影响的代码如下:

public abstract class ChallengeReportDelegation implements Runnable
{
    private ChallengeReport fromChallengeReport = null;
    private ChallengeReport toChallengeReport = null;

    @Override
    public abstract void run();

    public ChallengeReport getFromChallengeReport()
    {
        return fromChallengeReport;
    }

    public ChallengeReport getToChallengeReport()
    {
        return toChallengeReport;
    }

    public void setFromChallengeReport(ChallengeReport fromChallengeReport)
    {
        this.fromChallengeReport = fromChallengeReport;
    }

    public void setToChallengeReport(ChallengeReport toChallengeReport)
    {
        this.toChallengeReport = toChallengeReport;
    }
}
// Record challenge
this.challengesManager.recordChallenge(whoSentRequest, whoConfirmedRequest,
                            new ChallengeReportDelegation()
                            {
                                @Override
                                public void run()
                                {
                                    ChallengeReport fromReport = getFromChallengeReport();
                                    ChallengeReport toReport = getToChallengeReport();
                                    sendMessage(whoSentRequest, new Message(MessageType.CHALLENGE_REPORT, String.valueOf(fromReport.winStatus), String.valueOf(fromReport.challengeProgress), String.valueOf(fromReport.scoreGain)));
                                    sendMessage(whoConfirmedRequest, new Message(MessageType.CHALLENGE_REPORT, String.valueOf(toReport.winStatus), String.valueOf(toReport.challengeProgress), String.valueOf(toReport.scoreGain)));
                                }
                            });
private void complete()
    {
        stopTranslations();

        int fromStatus;
        int toStatus;
        if (this.fromScore > this.toScore)
        {
            fromStatus = 1;
            toStatus = -1;
        }
        else if (this.fromScore < this.toScore)
        {
            fromStatus = -1;
            toStatus = 1;
        }
        else
        {
            fromStatus = 0;
            toStatus = 0;
        }

        this.completionOperation.setFromChallengeReport(new ChallengeReport(this.from, fromStatus,this.fromTranslationsProgress, this.fromScore));
        this.completionOperation.setToChallengeReport(new ChallengeReport(this.to, toStatus, this.toTranslationsProgress, this.toScore));
        this.completionOperation.run();
    }

上面的代码在 运行 方法中的代码的最后一部分执行时引发 NullPointerException

[编辑]

NullPointerException 异常被抛出,因为 getFromChallengeReport()getToChallengeReport() (代码的第二部分)最初 return null (当 Runnable 被定义并作为参数传递时), 但它们会 return 在调用时保持一致的值 run() (代码的第三部分)

[EDIT2]

我在这个简单的代码中重现了这种情况:

public class TestEvaluation
{
    public static void main(String[] args) throws InterruptedException
    {
        Middle middle = new Middle();

        middle.register(new Task() {
            @Override
            public void run() {
                System.out.println("a is: " +  getA());
                System.out.println("b is: " +  getB());
            }
        });

        Thread.sleep(2000);
    }

    abstract static class Task implements Runnable
    {
        private int a = 0;
        private int b = 0;

        public int getA() {
            return a;
        }

        public void setA(int a) {
            this.a = a;
        }

        public int getB() {
            return b;
        }

        public void setB(int b) {
            this.b = b;
        }

        @Override
        abstract public void run();
    }

    static class Middle
    {
        private ScheduledThreadPoolExecutor pool = new ScheduledThreadPoolExecutor(1);

        public void register(Task task)
        {
            Leaf leaf = new Leaf(new Task() {
                @Override
                public void run() {
                    System.out.println("Middle");
                    task.run();
                }
            });
            pool.schedule(leaf, 1, TimeUnit.SECONDS);
        }
    }

    static class Leaf implements Runnable
    {
        public Task task;

        public Leaf(Task task)
        {
            this.task = task;
        }

        @Override
        public void run()
        {
            task.setA(5);
            task.setB(5);
            System.out.println("Leaf");
            task.run();
        }
    }
}

我想要实现的行为是打印

Leaf
Middle
a is: 5
b is: 5

但这就是我得到的

Leaf
Middle
a is: 0
b is: 0

如果您想立即评估某事。我建议根本不要使用 Runnable。这听起来像是一种反模式,当你想要的只是 value/invocation.

时试图传递代码

此外,尝试改用 Callable 或 Supplier,因为您显然对从子例程返回一些值感兴趣。

一个非常简单的例子。让我们创建一个带有字段的 运行nable。

public static void main (String[] args) {
    var x = new Runnable(){
        int a = 0;
        int getA(){
               return a;
        }
        void setA(int v){
            a = v;
        }
        public void run(){
            System.out.println("A : " + getA());
        }
    };
    x.run();
    x.setA(5);
    x.run();
}

第一次是0,第二次是5,因为getA是在运行调用的时候求值的。

我找到了这个问题的有效解决方案,对于那些来自函数式编程的人来说可能微不足道。

根据上次编辑的例子([EDIT2])

import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

public class TestEvaluation
{
    public static void main(String[] args) throws InterruptedException
    {
        Middle middle = new Middle();

        middle.register(new Consumer<Values>() {
            @Override
            public void accept(Values values) {
                System.out.println("a is: " +  values.getA());
                System.out.println("b is: " +  values.getB());
            }
        });

        Thread.sleep(2000);
    }

    static class Values
    {
        private int a = 0;
        private int b = 0;

        public int getA() {
            return a;
        }

        public void setA(int a) {
            this.a = a;
        }

        public int getB() {
            return b;
        }

        public void setB(int b) {
            this.b = b;
        }
    }

    static class Middle
    {
        private ScheduledThreadPoolExecutor pool = new ScheduledThreadPoolExecutor(1);

        public void register(Consumer<Values> passed)
        {
            Consumer<Values> middleConsumer = new Consumer<Values>() {
                @Override
                public void accept(Values values) {
                    System.out.println("Middle");
                    passed.accept(values);
                }
            };

            Leaf leaf = new Leaf(middleConsumer);
            pool.schedule(leaf, 1, TimeUnit.SECONDS);
        }
    }

    static class Leaf implements Runnable
    {
        public Consumer<Values> task;

        public Leaf(Consumer<Values> task)
        {
            this.task = task;
        }

        @Override
        public void run()
        {
            Values values = new Values();
            values.setA(5);
            values.setB(5);
            System.out.println("Leaf");
            task.accept(values);
        }
    }
}

这段代码产生了我想要的行为。 希望这会对某人有所帮助。 干杯!