Java Runnable как оценка аргумента - PullRequest
0 голосов
/ 02 апреля 2020

Я разрабатываю клиент-сервер в 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;
    }
}
  • Здесь, где Runnable передается в качестве аргумента:
// 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)));
                                }
                            });
  • Принимающий объект сохраняет экземпляр ChallengeReportDelegation как completionOperation, подождите тайм-аут, затем выполните этот код.
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 при выполнении последней части кода в методе run.

[EDIT]

Исключение NullPointerException выбрасывается, поскольку оба getFromChallengeReport() и getToChallengeReport() (вторая часть кода) первоначально возвращает ноль (когда Runnable определен и передан в качестве аргумента), но они будут возвращать согласованные значения во время вызова run() (третья часть кода)

[ РЕДАКТИРОВАТЬ2]

Я воспроизвел ситуацию в следующем простом коде:

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

Ответы [ 3 ]

0 голосов
/ 02 апреля 2020

Очень простой пример. Давайте создадим бегущую с полем.

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 вычисляется при вызове run.

0 голосов
/ 02 апреля 2020

Я нашел рабочее решение этой проблемы, возможно, тривиальное для тех, кто прибывает из функционального программирования.

В соответствии с примером в последнем редактировании ([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);
        }
    }
}

Этот код производит поведение, которое я хочу. Надеюсь, это кому-нибудь поможет. Ура!

0 голосов
/ 02 апреля 2020

Если вы хотите сразу что-то оценить. Я бы предложил вообще не использовать работоспособный. Это звучит как апти-паттерн, пытающийся передать код, когда все, что вам нужно, это значение / вызов.

Кроме того, вместо этого попробуйте использовать Callable или Supplier, поскольку вы явно заинтересованы в возврате некоторых значений из подпрограмм,.

...