1

Unless I catch and re-catch the various Exceptions inside the Lambda Expression the code below does not compile. Can anyone help me understand why?

 protected void doGet(HttpServletRequest req, HttpServletResponse res)
    throws ServletException, IOException
    {  
        Consumer <String> forwardTo =(s) ->
        {
            RequestDispatcher rd= req.getRequestDispatcher(s);
            try {
                rd.forward(req, res);
            } catch (IOException|ServletException is) {
                try {
                    throw new Exception(is);
                } catch (Exception e) {
                }
            }
        };
6ton
  • 4,174
  • 1
  • 22
  • 37
Jerrolds
  • 67
  • 5
  • I am compiling with javac 1.8.0_66. The question really is since the doGet throws these Exceptions why do I have to catch them again? – Jerrolds Nov 23 '15 at 18:11
  • Lambdas are functions, which require their own exception handling. Althouhh `doGet` throws the `IOException`, it does not account for exceptions thrown in lambdas – Vince Nov 23 '15 at 18:12
  • I understand that but the req and res are the ones passed by doGet. – Jerrolds Nov 23 '15 at 18:15
  • Yes, the lambda uses those variables (which it can only do if the variables are effectively final, as they currently are). That does not mean `doGet` will throw the exception occuring in the lambda, simply because the lambda references some variables from `doGet`'s parameters – Vince Nov 23 '15 at 18:17

4 Answers4

1

Well doGet throws the exception but your lambda is not getting executed in doGet. You are just declaring a variable - which is different from executing the expression you wrote.

In pre java-8 this would be equivalent code:

protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
    Consumer<String> forwardTo = new Consumer<String>() {
            @Override
            public void accept(String t) {
                RequestDispatcher rd = req.getRequestDispatcher(t);
                try {
                    rd.forward(req, res);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
    };
}
6ton
  • 4,174
  • 1
  • 22
  • 37
  • I still am confused by why I have to re-catch the Exception – Jerrolds Nov 23 '15 at 18:21
  • Because you added code to rethrow the exception: throw new Exception(is);, Remove that line so you don't need to re-catch it – 6ton Nov 23 '15 at 18:22
  • Thanks!Change the subject slightly, I thought I understood this example – Jerrolds Nov 23 '15 at 18:55
  • Thanks!Change the subject slightly, I thought I understood this example import java.util.function.Function; class recursion { Function factorial_lambda; //static context public static void main(String[] args) { new recursion();} public recursion() { factorial_lambda=(i)->{ if (i==1) return 1; else return i*(factorial_lambda.apply(i-1)); }; System.out.println(factorial_lambda.apply(5)); } } I cannot define factorial_lambda outside of recursion because of self-reference – Jerrolds Nov 23 '15 at 19:02
  • If your factorial_lambda was written as an anonymous class you could reference it as this.apply(i-1). However "this" cannot be used inside a lambda to refer to itself, instead "this" points to the containing class. Related http://stackoverflow.com/questions/24202236/lambda-this-reference-in-java – 6ton Nov 23 '15 at 19:29
0

You can only throw RuntimeExceptions out of a lambda function. If you change throw new Exception(is); into throw new RuntimeException(is); you won't need to re-catch Exception.

One more reason to favor unchecked exceptions, especially when working with Java 8 streams.

Kayaman
  • 72,141
  • 5
  • 83
  • 121
0

The default functional interfaces in Java 8 are declared as throwing no checked exceptions. This means that you will need in one way or another take care of the checked exceptions that your code may throw.

If you don't have a hard dependency on the Java 8 functional interfaces, you can just write your own:

interface ThrowingConsumer<T,E extends Exception> { public void accept(T t) throws E;}

Then just use this instead of the Java 8 Consumer.

If you have hard dependencies on the Java 8 functional interfaces, you can always wrap the resulting exceptions:

public <T> Consumer<T> ignoreExceptions(ThrowingConsumer<T,?> consumer){
  return t -> {
    try{consumer.accept(t);}catch(Throwable t){/*ignored*/}
  };
}

You can use this method like this:

Consumer<String> forwardTo = ignoreExceptions(t -> 
       req.getRequestDispatcher(t).forward(req, res));

Or, you can rethrow them:

public <T> Consumer<T> rethrowExceptions(ThrowingConsumer<T,?> consumer){
  return t -> {
    try{consumer.accept(t);}catch(Throwable t){throw new RuntimeException(e);}
  };
}
Tassos Bassoukos
  • 16,017
  • 2
  • 36
  • 40
0

The ServletRequest.getRequestDispatcher method takes a String argument. Your code does not specify a generic type for Consumer so the type given for your lambda parameter "s" is an Object. If you declare the consumer like this: Consumer it should compile.

The reason you have to catch Exceptions is because the RequestDispatcher.forward method throws ServletException and IOException, both of which are what java calls a "Checked Exception". Checked exceptions MUST be handled by callers. Here is an article(there are many on the internet) about exceptions in Java. http://www.geeksforgeeks.org/checked-vs-unchecked-exceptions-in-java/

If you want to create a method that converts the checked exception into an unchecked one you can create a new RuntimeException as I did in the code below:

protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException 
{
    Consumer<String> forwardTo = (s) -> 
    {
        forward(req, res, s);
    };
}

private void forward(HttpServletRequest req, HttpServletResponse res, String s)
{
    RequestDispatcher rd = req.getRequestDispatcher(s); 
    try 
    { 
        rd.forward(req, res); 
    } 
    catch (IOException|ServletException is) 
    { 
        throw new RuntimeException(is);
    } 
}
Josh Chappelle
  • 1,558
  • 2
  • 15
  • 37