如何使用 java 以函数式风格实现 akka actor

How implement akka actor in functional style with java

我在 java 中实现了简单的计数器 actor:

public class CounterJavaActor extends UntypedActor {

    int count = 0;

    @Override
    public void onReceive(Object message) throws Exception {
        if (message.equals("incr")) {
            count += 1;
        } else if (message.equals("get")) {
            sender().tell(count, self());
        }
    }

}

在 coursera 的课程中​​ "Functional reactive programming in scala",我看到了计数器的功能实现:

/**
* Advantages:
* state change is explicit
* state is scoped to current behaviour
*/
class CounterScala extends Actor{

  def counter(n: Int) : Receive = {
    case "incr" => context.become(counter(n+1))
    case "get" => sender ! n
  }

  def receive = counter(0)
}

更新: 我的问题是,在 java 中,我无法像在 scala counter(n+1) 中那样进行追索函数调用。含义:

public class CounterJava8Actor extends AbstractActor {
    //counter(0) in scala
    private PartialFunction<Object, BoxedUnit> counter;

    private int n = 0;

    public CounterJava8Actor() {
        counter =
            ReceiveBuilder.
                matchEquals("get", s -> {
                    sender().tell(n, self());
                }).
                matchEquals("inc", s -> {
                    //become(counter(n+1) in scala
                    context().become(counter);
                }).build();
        receive(counter);
    }
}

可以用 java 以函数式风格实现它吗?

根据文档,您可以在 java 8

中使用 become/unbecome

http://doc.akka.io/docs/akka/snapshot/java/lambda-actors.html#become-unbecome

这里是从那里复制的示例代码

public class HotSwapActor extends AbstractActor {
  private PartialFunction<Object, BoxedUnit> angry;
  private PartialFunction<Object, BoxedUnit> happy;

  public HotSwapActor() {
    angry =
      ReceiveBuilder.
        matchEquals("foo", s -> {
          sender().tell("I am already angry?", self());
        }).
        matchEquals("bar", s -> {
          context().become(happy);
        }).build();

    happy = ReceiveBuilder.
      matchEquals("bar", s -> {
        sender().tell("I am already happy :-)", self());
      }).
      matchEquals("foo", s -> {
        context().become(angry);
      }).build();

    receive(ReceiveBuilder.
      matchEquals("foo", s -> {
        context().become(angry);
      }).
      matchEquals("bar", s -> {
        context().become(happy);
      }).build()
    );
  }
}

或者你可以使用 UntypedActor,就像这里的文档中解释的那样

http://doc.akka.io/docs/akka/snapshot/java/untyped-actors.html

public class Manager extends UntypedActor {

  public static final String SHUTDOWN = "shutdown";

  ActorRef worker = getContext().watch(getContext().actorOf(
      Props.create(Cruncher.class), "worker"));

  public void onReceive(Object message) {
    if (message.equals("job")) {
      worker.tell("crunch", getSelf());
    } else if (message.equals(SHUTDOWN)) {
      worker.tell(PoisonPill.getInstance(), getSelf());
      getContext().become(shuttingDown);
    }
  }

  Procedure<Object> shuttingDown = new Procedure<Object>() {
    @Override
    public void apply(Object message) {
      if (message.equals("job")) {
        getSender().tell("service unavailable, shutting down", getSelf());
      } else if (message instanceof Terminated) {
        getContext().stop(getSelf());
      }
    }
  };
}

要了解如何向 Procedure 添加参数,您可以查看此答案:

Akka/Java getContext().become with parameter?

这里是 java 8

的实际解决方案
private PartialFunction<Object, BoxedUnit> counter(final int n) {
    return ReceiveBuilder.
        matchEquals("get", s -> {
            sender().tell(n, self());
        }).
        matchEquals("inc", s -> {
            context().become(counter(n + 1));
        }).build();
}


public CounterJava8Actor() {
    receive(counter(0));
}