Ошибка Java при объединении лямбда-выражений и предложений multi-catch?
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();
}
}
Почему эта программа печатает "неправильно"? Если я удаляю лямбду или разбиваю фразу multi-catch, она выводит "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)
2 ответа
Это была исправленная ошибка в 1.8.0_20, начиная с 1.8.0_11:
Площадь: инструменты / Javac
Сводка: javac генерирует неверную таблицу исключений для операторов multi-catch внутри лямбдыИсправлена обработка try-catch с несколькими уловами внутри лямбды.
Фактический отчет об ошибке JDK-8036942
Что на самом деле пошло не так, так это предполагаемая потеря информации о типе в компиляторе:
LTM интенсивно использует erasure() во время переводов и отображения переменных. Эти операции удаления могут быть правильными в большинстве случаев, но это может привести к потере информации, как показывает этот случай. Также возможно, что такое интенсивное использование erasure() необходимо здесь, поскольку LTM применяется после TransTypes, который должен стирать большинство / все типы, поэтому мне интересно, может ли это быть ошибкой в TransTypes. Я думаю, что это должен оценить Роберт Филд, который в настоящее время поддерживает LTM, каков наилучший подход, поэтому я переназначу его так.
Что я вижу на 8u20 (я забыл дать параметр командной строки, и у меня больше нет 8u20, чтобы сделать это правильно):
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%
Правильный:
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
}
Эта ошибка исправлена в версии 1.8.0_20 https://bugs.openjdk.java.net/browse/JDK-8036942
Я могу скопировать его 1.8.0_11, и он исправлен с помощью 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
работает отлично
~$ 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