vic*_*nes 25 java lambda anonymous-inner-class java-8
在做一些基本的lambda练习时,一个看似相同的匿名内部类的输出给了我一个不同于lambda的输出.
interface Supplier<T> {
T get(T t);
}
Run Code Online (Sandbox Code Playgroud)
场景#1
Supplier<Integer> s1 = new Supplier<Integer>() {
@Override
public Integer get(Integer t) {
return t;
}
};
Supplier<Integer> s2 = t -> t;
System.out.println(s1.get(2));
System.out.println(s2.get(2));
Run Code Online (Sandbox Code Playgroud)
输出2和2.这里没什么新鲜的.
但是当我这样做时:
场景#2
Supplier<Integer> s1 = new Supplier<Integer>() {
@Override
public Integer get(Integer t) {
return t++;
}
};
Supplier<Integer> s2 = t -> t++;
System.out.println(s1.get(2));
System.out.println(s2.get(2));
Run Code Online (Sandbox Code Playgroud)
输出2和3
问题:两个输出不应该相同吗?我错过了什么吗?
为了完整起见: 场景#3
Supplier<Integer> s1 = new Supplier<Integer>() {
@Override
public Integer get(Integer t) {
return ++t;
}
};
Supplier<Integer> s2 = t -> ++t;
System.out.println(s1.get(2));
System.out.println(s2.get(2));
Run Code Online (Sandbox Code Playgroud)
输出3和3.这里也没什么新东西.
更新:仍然从1.8.0-b132获得相同的输出
更新#2:错误报告: https ://bugs.openjdk.java.net/browse/JDK-8038420
更新#3:错误已在javac中修复,您现在应该能够获得相同的结果.
Iva*_*nin 14
根据生成的字节码:
Java(TM)SE运行时环境(版本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;
Run Code Online (Sandbox Code Playgroud)
匿名课程:
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;
Run Code Online (Sandbox Code Playgroud)
如您所见,在从本地变量表(方法参数t)加载变量后的匿名类中,参数在另一个变量(astore_2)中运行时存储副本,然后使用此参数副本作为返回值.
Lambda方法不会复制参数(load - > unbox - > add 1 - > box - > store - > load - > return).
UPDATE
这肯定是一个javac bug.
我从http://hg.openjdk.java.net/jdk8u/jdk8u获得了源代码
匿名类和lambda转换为以下中间表示:
@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);
}
Run Code Online (Sandbox Code Playgroud)
在lambda生成的方法参数中标记为final,因为LambdaToMethod转换器将所有参数标记为FINAL(根据源代码LambdaTranslationContext.translate(...):1899).
然后让表达式构建器检查变量标志,如果它最后省略临时变量生成(根据源代码Lower.abstractRval(...):2277),因为修改被认为是禁止的.
可能的解决方案:
在lamda生成的方法中从局部变量(LambdaTranslationContext.translate(...):1894)和参数(LambdaTranslationContext.translate(...):1899)中删除FINAL标志:
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);
...
Run Code Online (Sandbox Code Playgroud)我删除了FINAL标志,并从以下测试中得到了预期结果:https://bugs.openjdk.java.net/browse/JDK-8038420
| 归档时间: |
|
| 查看次数: |
1842 次 |
| 最近记录: |