pekko/akka-actor/src/main/java/akka/japi/pf/ReceiveBuilder.java
Patrik Nordwall 4bd6b7aab1 improve AbstractActor, #21717
* Receive class that wraps PartialFunction, to avoid
  scary scala types
* move AbstractActorContext to AbstractActor.ActorContext
* converting docs, many, many UntypedActor
* removing UntypedActor docs
* add unit test for ReceiveBuilder
* MiMa filters
* consistent use of getContext(), self(), sender()
* rename cross references
* migration guide
* skip samples for now
* improve match type safetyi, add matchUnchecked
  * the `? extends P` caused code like this to compile:
    `match(String.class, (Integer i) -> {})`
  * added matchUnchecked, since it can still be useful (um, convenient)
    to be able to do:
    `matchUnchecked(List.class, (List<String> list) -> {})`
* eleminate some scala.Option
  * preRestart
  * findChild
  * ActorIdentity.getActorRef
2017-01-23 18:30:52 +01:00

232 lines
7 KiB
Java

/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package akka.japi.pf;
import scala.PartialFunction;
import scala.runtime.BoxedUnit;
import akka.actor.AbstractActor;
import akka.actor.AbstractActor.Receive;
/**
* Used for building a partial function for {@link akka.actor.Actor#receive() Actor.receive()}.
*
* There is both a match on type only, and a match on type and predicate.
*
* Inside an actor you can use it like this with Java 8 to define your receive method.
* <p>
* Example:
* </p>
* <pre>
* &#64;Override
* public Actor() {
* receive(ReceiveBuilder.
* match(Double.class, d -&gt; {
* sender().tell(d.isNaN() ? 0 : d, self());
* }).
* match(Integer.class, i -&gt; {
* sender().tell(i * 10, self());
* }).
* match(String.class, s -&gt; s.startsWith("foo"), s -&gt; {
* sender().tell(s.toUpperCase(), self());
* }).build()
* );
* }
* </pre>
*
*/
public class ReceiveBuilder {
private PartialFunction<Object, BoxedUnit> statements = null;
protected void addStatement(PartialFunction<Object, BoxedUnit> statement) {
if (statements == null)
statements = statement;
else
statements = statements.orElse(statement);
}
/**
* Build a {@link scala.PartialFunction} from this builder. After this call
* the builder will be reset.
*
* @return a PartialFunction for this builder.
*/
public Receive build() {
PartialFunction<Object, BoxedUnit> empty = CaseStatement.empty();
if (statements == null)
return new Receive(empty);
else
return new Receive(statements.orElse(empty)); // FIXME why no new Receive(statements)?
}
/**
* Return a new {@link ReceiveBuilder} with no case statements. They can be
* added later as the returned {@link ReceiveBuilder} is a mutable object.
*
* @return a builder with no case statements
*/
public static ReceiveBuilder create() {
return new ReceiveBuilder();
}
/**
* Add a new case statement to this builder.
*
* @param type
* a type to match the argument against
* @param apply
* an action to apply to the argument if the type matches
* @return a builder with the case statement added
*/
public <P> ReceiveBuilder match(final Class<P> type, final FI.UnitApply<P> apply) {
return matchUnchecked(type, apply);
}
/**
* Add a new case statement to this builder without compile time type check.
* Should normally not be used, but when matching on class with generic type
* argument it can be useful, e.g. <code>List.class</code> and
* <code>(List&lt;String&gt; list) -> {}</code>.
*
* @param type
* a type to match the argument against
* @param apply
* an action to apply to the argument if the type matches
* @return a builder with the case statement added
*/
@SuppressWarnings("unchecked")
public ReceiveBuilder matchUnchecked(final Class<?> type, final FI.UnitApply<?> apply) {
FI.Predicate predicate = new FI.Predicate() {
@Override
public boolean defined(Object o) {
return type.isInstance(o);
}
};
addStatement(new UnitCaseStatement<Object, Object>(predicate, (FI.UnitApply<Object>) apply));
return this;
}
/**
* Add a new case statement to this builder.
*
* @param type
* a type to match the argument against
* @param predicate
* a predicate that will be evaluated on the argument if the type
* matches
* @param apply
* an action to apply to the argument if the type matches and the
* predicate returns true
* @return a builder with the case statement added
*/
public <P> ReceiveBuilder match(final Class<P> type, final FI.TypedPredicate<P> predicate,
final FI.UnitApply<P> apply) {
return matchUnchecked(type, predicate, apply);
}
/**
* Add a new case statement to this builder without compile time type check.
* Should normally not be used, but when matching on class with generic type
* argument it can be useful, e.g. <code>List.class</code> and
* <code>(List&lt;String&gt; list) -> {}</code>.
*
* @param type
* a type to match the argument against
* @param predicate
* a predicate that will be evaluated on the argument if the type
* matches
* @param apply
* an action to apply to the argument if the type matches and the
* predicate returns true
* @return a builder with the case statement added
*/
@SuppressWarnings("unchecked")
public <P> ReceiveBuilder matchUnchecked(final Class<?> type, final FI.TypedPredicate<?> predicate,
final FI.UnitApply<P> apply) {
FI.Predicate fiPredicate = new FI.Predicate() {
@Override
public boolean defined(Object o) {
if (!type.isInstance(o))
return false;
else
return ((FI.TypedPredicate<Object>) predicate).defined(o);
}
};
addStatement(new UnitCaseStatement<Object, Object>(fiPredicate, (FI.UnitApply<Object>) apply));
return this;
}
/**
* Add a new case statement to this builder.
*
* @param object
* the object to compare equals with
* @param apply
* an action to apply to the argument if the object compares equal
* @return a builder with the case statement added
*/
public <P> ReceiveBuilder matchEquals(final P object, final FI.UnitApply<P> apply) {
addStatement(new UnitCaseStatement<Object, P>(new FI.Predicate() {
@Override
public boolean defined(Object o) {
return object.equals(o);
}
}, apply));
return this;
}
/**
* Add a new case statement to this builder.
*
* @param object
* the object to compare equals with
* @param predicate
* a predicate that will be evaluated on the argument if the object
* compares equal
* @param apply
* an action to apply to the argument if the object compares equal
* @return a builder with the case statement added
*/
public <P> ReceiveBuilder matchEquals(final P object, final FI.TypedPredicate<P> predicate,
final FI.UnitApply<P> apply) {
addStatement(new UnitCaseStatement<Object, P>(new FI.Predicate() {
@Override
public boolean defined(Object o) {
if (!object.equals(o))
return false;
else {
@SuppressWarnings("unchecked")
P p = (P) o;
return predicate.defined(p);
}
}
}, apply));
return this;
}
/**
* Add a new case statement to this builder, that matches any argument.
*
* @param apply
* an action to apply to the argument
* @return a builder with the case statement added
*/
public ReceiveBuilder matchAny(final FI.UnitApply<Object> apply) {
addStatement(new UnitCaseStatement<Object, Object>(new FI.Predicate() {
@Override
public boolean defined(Object o) {
return true;
}
}, apply));
return this;
}
}