Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java bug when combining lambdas and multi-catch clauses?

import java.io.*;             
import java.net.*;            

public class Test {           
    public static void main(String[] arguments) throws Exception {
        Runnable runnable = () -> {
            try {
                throwException();
            }
            catch (SocketException|EOFException exception) {
                System.err.println("wrong");
            }
            catch (IOException exception) {
                System.err.println("right");
            }
        };

        runnable.run();
    }                         

    private static void throwException() throws IOException {
        throw new NotSerializableException();
    }                         
}

Why does this program print "wrong"? If I remove the lambda, or if I break apart the multi-catch clause, then it prints "right".

$ javac -version
javac 1.8.0_11
$ java -version
java version "1.8.0_11"
Java(TM) SE Runtime Environment (build 1.8.0_11-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.11-b03, mixed mode)
like image 946
John Kugelman Avatar asked Sep 13 '14 01:09

John Kugelman


People also ask

How do you handle two exceptions in single catch?

Java allows you to catch multiple type exceptions in a single catch block. It was introduced in Java 7 and helps to optimize code. You can use vertical bar (|) to separate multiple exceptions in catch block.

Is there a way to catch multiple exceptions at once and without code duplication?

Catching Multiple Exceptions in a Single Catch Block To do that, we use the single-catch-block model and group the type of exceptions we want to handle. In this method, we pack all the potential exceptions at one catch block using the when keyword. It is a shorter way to declare exceptions to the catch block.

What is Rethrowing exception in Java?

If a catch block cannot handle the particular exception it has caught, we can rethrow the exception. The rethrow expression causes the originally thrown object to be rethrown.

Can one block handle multiple exceptions?

Handle Multiple Exceptions in a catch Block In Java SE 7 and later, we can now catch more than one type of exception in a single catch block. Each exception type that can be handled by the catch block is separated using a vertical bar or pipe | .


2 Answers

This was a fixed bug in 1.8.0_20, from 1.8.0_11:

Area: tools/javac
Synopsis: javac generates incorrect exception table for multi-catch statements inside a lambda

Handling of try-catch with multiple catches inside a lambda has been corrected.

The actual bug report is JDK-8036942

What actually went wrong was a supposed type information loss within the compiler:

LTM makes a heavy use of erasure() during translations and mapping of variables. These erasure operations may be correct in most cases but it may lead to an information loss as this case shows. It's also arguably that such an intense use of erasure() is needed here as LTM is applied after TransTypes which is supposed to erase most / all types, so I wonder if this may be a bug in TransTypes. I think that it should be evaluated by Robert Field, which is current maintainer of LTM, what is the best approach here, I will thus reassigning[sic] it to him.

What I see on 8u20 (I forgot to give a command-line parameter and no longer have 8u20 to do it correctly):

wlan1-loopback% /usr/lib/jvm/java-8-oracle/bin/javap -c Test
Compiled from "Test.java"
public class Test {
  public Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]) throws java.lang.Exception;
    Code:
       0: invokedynamic #2,  0              // InvokeDynamic #0:run:()Ljava/lang/Runnable;
       5: astore_1
       6: aload_1
       7: invokeinterface #3,  1            // InterfaceMethod java/lang/Runnable.run:()V
      12: return
}
wlan1-loopback% java Test 
right
wlan1-loopback% java -version
java version "1.8.0_20"
Java(TM) SE Runtime Environment (build 1.8.0_20-b26)
Java HotSpot(TM) 64-Bit Server VM (build 25.20-b23, mixed mode)
wlan1-loopback% 

Correct:

public class Test {
  public Test();
    descriptor: ()V
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]) throws java.lang.Exception;
    descriptor: ([Ljava/lang/String;)V
    Code:
       0: invokedynamic #2,  0              // InvokeDynamic #0:run:()Ljava/lang/Runnable;
       5: astore_1
       6: aload_1
       7: invokeinterface #3,  1            // InterfaceMethod java/lang/Runnable.run:()V
      12: return

  private static void throwException() throws java.io.IOException;
    descriptor: ()V
    Code:
       0: new           #4                  // class java/io/NotSerializableException
       3: dup
       4: invokespecial #5                  // Method java/io/NotSerializableException."<init>":()V
       7: athrow

  private static void lambda$main$0();
    descriptor: ()V
    Code:
       0: invokestatic  #6                  // Method throwException:()V
       3: goto          27
       6: astore_0
       7: getstatic     #9                  // Field java/lang/System.err:Ljava/io/PrintStream;
      10: ldc           #10                 // String wrong
      12: invokevirtual #11                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      15: goto          27
      18: astore_0
      19: getstatic     #9                  // Field java/lang/System.err:Ljava/io/PrintStream;
      22: ldc           #13                 // String right
      24: invokevirtual #11                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      27: return
    Exception table:
       from    to  target type
           0     3     6   Class java/net/SocketException
           0     3     6   Class java/io/EOFException
           0     3    18   Class java/io/IOException
}
like image 107
nanofarad Avatar answered Sep 19 '22 23:09

nanofarad


This bug is fixed with 1.8.0_20 https://bugs.openjdk.java.net/browse/JDK-8036942

I am able to replicate it 1.8.0_11 and it is fixed with 1.8.0_20

$ javac -version
javac 1.8.0_11
$ java -version
java version "1.8.0_11"
Java(TM) SE Runtime Environment (build 1.8.0_11-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.11-b03, mixed mode)
$ javac Test.java
$ java Test
wrong

works fine

~$ javac -version
javac 1.8.0_20
$ javac Test.java 
$ java -version
java version "1.8.0_20"
Java(TM) SE Runtime Environment (build 1.8.0_20-b26)
Java HotSpot(TM) 64-Bit Server VM (build 25.20-b23, mixed mode)
$ java Test
right
like image 43
jmj Avatar answered Sep 18 '22 23:09

jmj