I was looking for switch over type for java, and I found a solution here: Switch over type in java
But the issue, solution was not really like a switch
with break
s, sure this will happen because I loop over Consumer
s , I want to know a good way to modify my current code or clean alternative to have something to handle my class type, and break after handle first match and the default is to handle the unmatched only ( here it is the parent class StripeObject
) .
My business is to handle stripe events from webhooks, I'm using stripe-java, so my code now something like this:
public void handleWebHookEvent(Event eventJson) throws CardException, APIException, AuthenticationException, InvalidRequestException, APIConnectionException {
Stripe.apiKey = configuration.getString(AppConstants.STRIPE_API_KEY);
// Verify the event by fetching it from Stripe
Event event = Event.retrieve(eventJson.getId());
Logger.debug(AppUtils.concatStrings("|-> Event Received: [", event.getId(), "] Type: [", event.getType(), "] Request ID: [", event.getRequest(), "]"));
handleEventObject(event.getData().getObject());
}
private void handleEventObject(StripeObject stripeObject) {
switchType(stripeObject,
caze(Customer.class, this::handleEventObject),
caze(Card.class, this::handleEventObject),
caze(Charge.class, this::handleEventObject),
caze(Transfer.class, this::handleEventObject)
caze(StripeObject.class, object -> Logger.debug(AppUtils.concatStrings("|--> !!! Not Handled StripeObject !!! [", object.toString(), "]")))
);
}
private void handleEventObject(Customer customer) {
Logger.debug(AppUtils.concatStrings("|--> Customer ID <Description> : [", customer.getId(), " <", customer.getDescription(), ">]"));
}
private void handleEventObject(Card card) {
Logger.debug(AppUtils.concatStrings("|--> Card ID <Brand> : [", card.getId(), " <", card.getBrand(), ">]"));
}
private void handleEventObject(Charge charge) {
Logger.debug(AppUtils.concatStrings("|--> Charge ID <Description , Customer , Amount> : [", charge.getId(), " <", charge.getDescription(), " , ", charge.getCustomer() ," , ", charge.getAmount(), ">]"));
}
private void handleEventObject(Transfer transfer) {
Logger.debug(AppUtils.concatStrings("|--> Transfer ID <Description , Destination , Amount> : [", transfer.getId(), " <", transfer.getDescription(), " , ", transfer.getDestination() ," , ", transfer.getAmount(), ">]"));
}
@SuppressWarnings("unchecked")
private static <T> void switchType(StripeObject stripeObject, Consumer... consumers) {
for (Consumer consumer : consumers)
consumer.accept(stripeObject);
}
private static <T> Consumer caze(Class<T> clazz, Consumer<T> consumer) {
return obj -> Optional.of(obj).filter(clazz::isInstance).map(clazz::cast).ifPresent(consumer);
}
In the current code, if the evnet was Customer as example it will print :
|--> Customer ID <Description> ... etc
Then
|--> !!! Not Handled StripeObject !!! ... etc
It must print only the first line, not the second if match.
You only have to change the methods
switchType
andcaze
to use aPredicate
instead of aConsumer
, so the loop can stop at the first match. The rest of the code doesn’t need any change:Note that this still differs from the
switch
semantics, as aswitch
statement precludes ambiguous cases in general. Here, one of the types still could be a subtype of another, so, unlike aswitch
statement, the order ofcase
statements does matter then.If all cases are distinct, this is not an issue.