1

I have the below code

import java.util.function.BiConsumer;

public class ExceptionHandlingLambda {

    public static void main(String[] args) {
        int [] someNumbers = { 1, 2, 3, 4 };
        int key = 2;

        process(someNumbers, key, (v,k) -> {
            try{
                System.out.println(v/k);
            }
            catch(ArithmeticException e){
                //handle exception
            }
        });

    }

    private static void process(int[] someNumbers, int key, BiConsumer<Integer, Integer> consumer) {
        for (int i : someNumbers) { 
          //can put a try catch here but this is generic and we don't know what exception we are catching.          
            consumer.accept(i, key);
        }

    }
}

I am trying to handle some exception in a lambda. From above I have handled an exception within a lambda. I think it makes my lambda look kind of messy.

I could also handle the exception in the process method but it would be generic and we wouldn't know what exception we are handling for.

Any other better way to handle this in lambda to have cleaner code? Any suggestions would be much appreciated.

Guru
  • 1,653
  • 1
  • 7
  • 14
  • 2
    Use a method reference – Michael Feb 14 '18 at 21:07
  • Why not put the entire method call inside the try block? – shmosel Feb 14 '18 at 21:07
  • 1
    @Michael How is that at all relevant? – shmosel Feb 14 '18 at 21:08
  • @shmosel yes I can do that but it woudn't be specific to the lambda if that makes sense. – Guru Feb 14 '18 at 21:11
  • Possible duplicate of [Java 8 Lambda function that throws exception?](https://stackoverflow.com/questions/18198176/java-8-lambda-function-that-throws-exception) – tsolakp Feb 14 '18 at 21:19
  • 2
    You can declare static method `divide(Integer v, Integer k) { try{ System.out.println(v/k); } catch(ArithmeticException e){ //handle exception } }`, and now you can just use `process(someNumbers, key, ExceptionHandlingLambda::divide)` I think this is what @Michael is meaning by suggesting to use method reference – Alanpatchi Feb 14 '18 at 21:21
  • Thank you everyone. Having a wrapper function with a method reference is a great suggestion. – Guru Feb 14 '18 at 21:31
  • If you expect `v` to be zero, you should use `if(v!=0) System.out.println(v/k); else /* handle special case */` instead of catching `ArithmeticException`. In contrast, if you don’t expect it, don’t catch it. – Holger Feb 15 '18 at 08:34

2 Answers2

2

After the suggestion from some great and kind minds I have an answer. Hope this helps someone.

process(someNumbers, key, wrapperLambda((v, k) -> System.out.println(v / k))); 

//with method reference 
process(someNumbers, key, ExceptionHandlingLambda::wrapperLambda);

A wrapper function for lambda which accepts a lambda and return a lambda with a try catch which makes it much cleaner.

private static BiConsumer<Integer, Integer> wrapperLambda(BiConsumer<Integer, Integer> consumer) {
        //creating a new lambda and return.
        // return (v,k) -> System.out.println(v+k); this would replace the lambda with addition lambda (v+k)

        return (v, k) ->  {
            try {
                consumer.accept(v, k); //execute whatever is passed in. 
            }
            catch (ArithmeticException e) {
                System.out.println("Exception caught in wrapper lambda");
            }

        };
}
Guru
  • 1,653
  • 1
  • 7
  • 14
  • That is not what Michael and boobalan were suggesting. – VGR Feb 14 '18 at 22:15
  • @VGR I have added the method reference as well. Looks cleaner now. – Guru Feb 15 '18 at 14:08
  • 1
    Still incorrect. Remove **all** occurrences of `->` from your code. Your private method’s signature should be `void methodName(Integer v, Integer k)`, and its body should only consist of a try/catch whose body is `System.out.println(v / k);`. No lambdas should be present and there should be no usage of BiConsumer in the private method. The method will implicitly conform to the BiConsumer functional interface and thus a reference to it will qualify as a BiConsumer instance. – VGR Feb 15 '18 at 18:07
-1

Apply try catch in your process method and pass an additional argument to the method i.e. the exception class for which you want to handle the exception.

Process method would look like

private static void process(int[] someNumbers, int key, BiConsumer<Integer, Integer> consumer, Class<E> clazz) {
    for (int i : someNumbers) { 
       try{
        consumer.accept(i, key);
     } catch(Exception ex) {
         try {
            E exCast = clazz.cast(ex);
            System.err.println(
              "Exception occured : " + exCast.getMessage());
        } catch (ClassCastException ccEx) {
            throw ex;
        }
      }
    }

}

This way your lambda would not look messy and you can decide which exception to be handled at the time of calling.

Jayant Narwani
  • 101
  • 1
  • 7
  • private static void process(int[] someNumbers, int key, BiConsumer consumer) { for (int i : someNumbers) { //can put a try catch here but this is generic and we don't know what exception we are catching. consumer.accept(i, key); } } – Guru Feb 14 '18 at 21:40
  • It is not generic If you parse `ArithmeticException` in an argument. then it will only handle `ArithmeticException` else will throw a `ClassCastException`. In second catch block, you can write additional logic to handle exceptions other than `ArithmeticException` – Jayant Narwani Feb 14 '18 at 21:50