Ishant Gaurav
Ishant Gaurav

Reputation: 1203

Convert looping into lambda and throw exception

How can i write below code using lambda expression in java8. I am new to Java 8.

for (GlobalPricingRequest globalPricingRequest : globalPricingRequests) {
    BigDecimal feePerTrans = globalPricingRequest.getFeePerTransact();
    if (feePerTrans != null && feePerTrans.intValue() < 0) {
        throw ExceptionHelper.badRequest("Fee Per Transaction can't be less than zero");
    }
    List<EventTypePricingMapping> eventTypePricingMappings = globalPricingRequest.getEventTypePricingList();
    for (EventTypePricingMapping eventTypePricingMapping : eventTypePricingMappings) {
        BigDecimal feePerRevenue = eventTypePricingMapping.getFeePerRevenue();
        if (feePerRevenue != null && feePerRevenue.intValue() < 0) {
            throw ExceptionHelper.badRequest("Fee Per Transaction can't be less than zero");
        }
        if (eventTypePricingMapping.getFeePerRevenue().intValue() < 0) {
            throw ExceptionHelper.badRequest("Fee Per Transaction can't be less than zero");
        }
    }
}

I have tried the below code as yet as per the suggestion . Is there any other thing which we can improve in this code to write it using lambdas more.

globalPricingRequests.forEach((globalPricingRequest) -> {
    if (checkIfValueLessThanZero(globalPricingRequest.getFeePerTransact())) {
        throw ExceptionHelper.badRequest("Fee Per Transaction can't be less than zero");
    }
    List<EventTypePricingMapping> eventTypePricingMappings = globalPricingRequest.getEventTypePricingList();
    eventTypePricingMappings.forEach((eventTypePricingMapping) -> {
        if (checkIfValueLessThanZero(eventTypePricingMapping.getFeePerRevenue())) {
            throw ExceptionHelper.badRequest("Fee Per Transaction can't be less than zero");
        }
        if (checkIfValueLessThanZero(eventTypePricingMapping.getFeePerReg())) {
            throw ExceptionHelper.badRequest("Fee Per Transaction can't be less than zero");
        }
    });
});


private boolean checkIfValueLessThanZero(Object object) {
    if (object instanceof BigDecimal) {
       if (object != null && ((BigDecimal) object).intValue() < 0) {
           return true;
       }
    }
    return false;
}

Upvotes: 5

Views: 239

Answers (4)

Naman
Naman

Reputation: 31878

You could use stream twice and improve the readability of your code as :

Predicate<BigDecimal> feeCheck =
        feePerTransactOrRevenue -> feePerTransactOrRevenue != null
                && feePerTransactOrRevenue.intValue() < 0;

boolean globalRequestCheck = globalPricingRequests.stream()
        .map(GlobalPricingRequest::getFeePerTransact)
        .anyMatch(feeCheck); 

boolean eventTypeCheck = globalPricingRequests.stream()
        .map(GlobalPricingRequest::getEventTypePricingList)
        .flatMap(List::stream)
        .map(EventTypePricingMapping::getFeePerRevenue)
        .anyMatch(feeCheck);

// if any of the element matches the condition, throw the exception
if (globalRequestCheck || eventTypeCheck) { 
    throw ExceptionHelper.badRequest("Fee Per Transaction can't be less than zero");
}

Upvotes: 2

Oleg Cherednik
Oleg Cherednik

Reputation: 18245

Problem

Your problem is not with lambdas, but with code organisation. You have a data, i.e. List<GlobalPricingRequest> and a set of validation rules. All you need to do it to apply these valdation rules to the given data.

This approach give you flexibility to add or remove validation rules easily. And test or check each rule separately.

Solution

Optimal solution is to split each validation into separate class.

First, create a manager and interface for validation rule:

public final class GlobalPricingRequestValidationManager {

    private final List<ValidationRule> validationRules =
            Arrays.asList(
                new TransactionFeeEqualOrGreaterThanZeroValidationRule(),
                new RevenueFeeEqualOrGreaterThanZeroValidationRule());

    public void validate(List<GlobalPricingRequest> globalPricingRequests) {
        validationRules.forEach(validationRule -> validationRule.validate(globalPricingRequests));
    }

    public interface ValidationRule {

        void validate(List<GlobalPricingRequest> globalPricingRequests);
    }

}

Second, implement each validation rule in the separate class (was added to the manager):

public final class TransactionFeeEqualOrGreaterThanZeroValidationRule implements GlobalPricingRequestValidationManager.ValidationRule {

    @Override
    public void validate(List<GlobalPricingRequest> globalPricingRequests) {
        if (globalPricingRequests.stream()
                                 .map(GlobalPricingRequest::getFeePerTransact)
                                 .filter(Objects::nonNull)
                                 .anyMatch(val -> val.signum() == -1)))
            throw ExceptionHelper.badRequest("Fee Per Transaction can't be less than zero");
    }
}

public final class RevenueFeeEqualOrGreaterThanZeroValidationRule implements GlobalPricingRequestValidationManager.ValidationRule {

    @Override
    public void validate(List<GlobalPricingRequest> globalPricingRequests) {
        if (globalPricingRequests.stream()
                                 .map(GlobalPricingRequest::getEventTypePricingList)
                                 .flatMap(List::stream)
                                 .map(EventTypePricingMapping::getFeePerRevenue)
                                 .filter(Objects::nonNull)
                                 .anyMatch(val -> val.signum() == -1)))
            throw ExceptionHelper.badRequest("Fee Per Transaction can't be less than zero");

    }
}

Clinet code:

GlobalPricingRequestValidationManager validationManager = new GlobalPricingRequestValidationManager();
List<GlobalPricingRequest> globalPricingRequests = Collections.emptyList();
validationManager.validate(globalPricingRequests);

Upvotes: 4

Ousmane D.
Ousmane D.

Reputation: 56423

This type of validation you're performing is better via the imperative approach nevertheless we can utilise lambdas where appropriate.

First, I would isolate the repetitive conditions to a local predicate with the use of signum as also suggested by @Thomas Kläger under the post as it's more appropriate in this specific case than intValue.

Predicate<BigDecimal> criteria = b -> b != null && b.signum() < 0;

Then your imperative approach would look like:

for (GlobalPricingRequest globalPricingRequest : globalPricingRequests) {
      isValidOrElseThrowBadRequestException(globalPricingRequest.getFeePerTransact(), criteria);
      for (EventTypePricingMapping eventTypePricingMapping : globalPricingRequest.getEventTypePricingList()) {
          isValidOrElseThrowBadRequestException(eventTypePricingMapping.getFeePerRevenue(), criteria);
      }
}

Where isValidOrElseThrow is defined as:

public static void isValidOrElseThrowBadRequestException(BigDecimal data, Predicate<BigDecimal> criteria) throws Exception { // change the exception to the specific one you're using 
       if(criteria.test(data)) throw ExceptionHelper.badRequest("Fee Per Transaction can't be less than zero");
}

Just with a few isolations here and there we're able to make the code more readable.

Upvotes: 2

Khalid Shah
Khalid Shah

Reputation: 3232

globalPricingRequest.forEach(data -> {
      if (data.getFeePerTransact() != null && data.getFeePerTransact().intValue() < 0) {
        // you can only throw unchecked exception in lambda function
        throw ExceptionHelper.badRequest("Fee Per Transaction can't be less than zero");
      }
      data.getEventTypePricingList().forEach(event-> {
        if (event.getFeePerRevenue() != null && event.getFeePerRevenue().intValue() < 0) {
          // you can only throw unchecked exception in lambda function
          throw ExceptionHelper.badRequest("Fee Per Transaction can't be less than zero");
        }// this if statemnt in your code is twice
      });
    });

Upvotes: 0

Related Questions