Ответ 1
В соответствии с созданным байт-кодом:
Java (TM) SE Runtime Environment (сборка 1.8.0-b132)
Lambda:
private static java.lang.Integer lambda$main$0(java.lang.Integer);
descriptor: (Ljava/lang/Integer;)Ljava/lang/Integer;
flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
Code:
stack=2, locals=2, args_size=1
0: aload_0
1: invokevirtual #9 // Method java/lang/Integer.intValue:()I
4: iconst_1
5: iadd
6: invokestatic #6 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
9: dup
10: astore_0
11: astore_1
12: aload_0
13: areturn
LineNumberTable:
line 20: 0
LocalVariableTable:
Start Length Slot Name Signature
0 14 0 t Ljava/lang/Integer;
Анонимный класс:
public java.lang.Integer get(java.lang.Integer);
descriptor: (Ljava/lang/Integer;)Ljava/lang/Integer;
flags: ACC_PUBLIC
Code:
stack=2, locals=4, args_size=2
0: aload_1
1: astore_2
2: aload_1
3: invokevirtual #2 // Method java/lang/Integer.intValue:()I
6: iconst_1
7: iadd
8: invokestatic #3 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
11: dup
12: astore_1
13: astore_3
14: aload_2
15: areturn
LineNumberTable:
line 16: 0
LocalVariableTable:
Start Length Slot Name Signature
0 16 0 this LTest$1;
0 16 1 t Ljava/lang/Integer;
Как вы можете видеть, в анонимном классе после загрузки переменной из локальной таблицы переменных (параметр метода t) среда выполнения хранит копию параметра в другой переменной (astore_2), а затем используйте эту копию параметра как возвращаемое значение.
Метод Lambda не делает копию параметра (load → unbox → добавить 1 → box → store → load → return).
UPDATE
Это определенно ошибка javac.
Я получил источник http://hg.openjdk.java.net/jdk8u/jdk8u
Анонимный класс и лямбда преобразуются в следующие промежуточные представления:
@Override()
public Integer get(Integer t) {
return (let /*synthetic*/ final Integer $112619572 = t in
(let /*synthetic*/ final Integer $1295226194 = t = Integer.valueOf((int)(t.intValue() + 1)) in $112619572));
}
/*synthetic*/ private static Integer lambda$main$0(final Integer t) {
return (let /*synthetic*/ final Integer $1146147158 = t = Integer.valueOf((int)(t.intValue() + 1)) in t);
}
В параметре метода генерируемого лямбда, отмеченном как final, поскольку LambdaToMethod переводчик маркирует все параметры как FINAL (согласно исходному коду LambdaTranslationContext.translate(...): 1899).
Затем пусть выражение builder проверяет флаги переменных и когда, если его окончательное исключает создание временной переменной (в соответствии с исходным кодом Lower.abstractRval(...): 2277), поскольку модификация считается запрещенной.
Возможные решения:
- Изменение параметра Forbid внутри lambda или
-
Удалить флаг FINAL из локальной переменной (LambdaTranslationContext.translate(...): 1894) и параметр (LambdaTranslationContext.translate(...): 1899) в методе, генерируемом lamda:
case LOCAL_VAR: ret = new VarSymbol(FINAL, name, types.erasure(sym.type), translatedSym); ... case PARAM: ret = new VarSymbol(FINAL | PARAMETER, name, types.erasure(sym.type), translatedSym); ...
Я удалил флаг FINAL и получил ожидаемые результаты тестов: https://bugs.openjdk.java.net/browse/JDK-8038420