Search code examples
javaakka

Why does Classic Akka fail to use Java's type casting when creating actors?


I'm encountering an issue while working with Classic Akka in Java. It seems that Akka is unable to use Java's built-in type casting when creating actors and using .tell().

Below is a simplified version of my code:

Main Class:

import akka.actor.ActorSystem;
import akka.actor.ActorRef;
import akka.actor.Props;
import java.util.UUID;

public class Main {
    public static void main(String[] args){
        ActorSystem system = ActorSystem.create("system");
        ActorRef actorA = system.actorOf(Props.create(ActorA.class , 0), UUID.randomUUID().toString());
        actorA.tell(new ActorA.MessageA(7,3),ActorRef.noSender());
    }
}

Actor:

import akka.actor.UntypedAbstractActor;

public class ActorA extends UntypedAbstractActor {
    private long result;
    public ActorA(long StartValue) {
    }
    //on method: MessageA
    public static final class MessageA {
        public final double paramD;
        public final long paramL;
        public MessageA(double paramD, long paramL) {
            this.paramD = paramD;
            this.paramL = paramL;
        }
    }
    private void onMessageA(double paramD,long paramL) {
        System.out.println(paramD);
        System.out.println(paramL);
    }
    public void onReceive(Object message) {
        if (message instanceof MessageA MessageAMsg) {
            onMessageA(MessageAMsg.paramD, MessageAMsg.paramL);
        } else {
            unhandled(message);
        }
    }
}

However, when I run this code, I encounter the following error:

Exception in thread "main" java.lang.IllegalArgumentException: no matching constructor found on class output.ActorA for arguments [class java.lang.Integer]
...

It seems that Akka is expecting an Integer argument instead of a long when creating the ActorA instance. Same goes for Double and Integer. I'm confused because Java usually handles type casting implicitly. Is there a specific reason why Akka behaves this way, and how can I resolve this issue?

Any insights or suggestions would be greatly appreciated. Thank you!


Solution

  • Using Props.create, as in Props.create(ActorA.class , 0) entails reflectively constructing the actor, which means that the normal Java type-promotion doesn't come "for free". Such promotion would have to be manually implemented by Props.create as a special case.

    Since the reflective form is not the recommended practice, it's unlikely that that special case would be added (though I suspect that a PR implementing that special case may well be accepted; note that while I am employed by Lightbend, I am not in a position to make any sort of prediction whether this acceptance would happen).

    If using Props.create(ActorA.class, () -> new ActorA(0)), the usual Java promotions would apply. Since main is static, the caveat about that approach inadvertently closing over state doesn't apply, but to address that, one could do something like:

    public class ActorA extends UntypedAbstractActor {
      public static Props props(long startValue) {
        return Props.create(ActorA.class, () -> new ActorA(startValue));
      }
    }