Release 2.0.0 of JActor will be characterized as providing classifications of requests: constrained and initialization. Constrained requests can be invoked by the Internals.call or Actor.acceptCall methods, while "unconstrained" requests must be invoked by the Internals.send, Internals.sendEvent or Actor.acceptRequest methods. The big advantage of initialization and constrained requests (and events) being that we are free from having to deal with callbacks to handle responses. The call and acceptCall methods directly return the results (the results from sending an event being ignored).
There are two basic types of constrained request: synchronous (new) and concurrent. Synchronous requests can only be passed using a call or acceptCall method when the sender and receiver use the same mailbox; concurrent requests must be thread safe and can only access concurrent data structures.
Initialization requests (new) can only be passed during the initialization phase of an actor, and the initialization phase ends when an actor receives a "non-initialization" request. Initialization requests are processed on the same thread as the sender under the assumption that initialization requests are sent one-at-a-time, so thread safety is not an issue.
The result of classifying requests as constrained or initialization is that we can then classify more requests as constrained or initialization. This is a cascading effect that, in many cases, frees us from the mess inherent in using anonymous callback classes. It is also faster, as GC no longer needs to collect all those callback instances.
I have seen such cascading effects a number of times. They are always delightful, as the code ends up being ever so much simpler. Such cascades occur when you can identify constraints that simplify processing. A rich API that supports a number of constraints makes simple things fast and easy to use. This contrasts nicely with API that are rich in features, i.e. unconstrained.