Lambda的行为与匿名内部类不同 [英] Lambda behaving differently than anonymous inner class

查看:352
本文介绍了Lambda的行为与匿名内部类不同的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在做一些基本的lambda练习时,一个明显相同的匿名内部类的输出给了我一个不同于lambda的输出。

While doing some basic lambda exercises, the output from an apparently identical anonymous inner class was giving me a different output than the lambda.

interface Supplier<T> {

    T get(T t);
}

场景#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));

输出 2 2 。这里没什么新东西。

Outputs 2 and 2. Nothing new here.

但当我这样做时:

场景#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));

输出 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));

输出 3 3 。此处也没有新内容。

Outputs 3 and 3. Nothing new here as well.

更新:仍然从1.8.0-b132获得相同的输出

更新#2:错误报告: https://bugs.openjdk .java.net / browse / JDK-8038420

更新#3:该错误已在javac中修复,您应该可以获得现在结果相同。

推荐答案

根据生成的字节码:


Java(TM)SE运行时环境(版本1.8.0-b132)

Java(TM) SE Runtime Environment (build 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 )中参数的运行时存储副本,然后将此参数副本用作返回值。

As you can see, in anonymous class after loading variable from local variable table (method parameter t) runtime store copy of parameter in another variable (astore_2) and then use this copy of parameter as returning value.

Lambda方法不会复制参数(load - > unbox - > add 1 - > box - > store - > load - > return)。

Lambda method doesn't make copy of parameter (load -> unbox -> add 1 -> box -> store -> load -> return).

更新

这绝对是一个javac错误。

It's definitely a javac bug.

我从http://hg.openjdk.java.net/jdk8u/jdk8u

匿名类和lambda转换为以下中间表示:

Anonymous class and lambda converts to following intermediate representations:

@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);
}

在lambda生成的方法参数中标记为final,因为LambdaToMethod转换器
将所有参数标记为FINAL(根据源代码 LambdaTranslationContext.translate(...):1899 )。

In lambda generated method parameter marked as final, because LambdaToMethod translator marks all parameters as FINAL (according source code LambdaTranslationContext.translate(…):1899).

然后让表达式构建器检查变量标志以及何时如果它最后省略临时变量生成
(根据源代码 Lower.abstractRval(...):2277 ),因为修改被认为是禁止的。

Then let expression builder checks variable flags and when if it’s final omits temporary variable generation (according source code Lower.abstractRval(…):2277), because modification considered to be prohibited.

可能的解决方案:


  1. 禁止在lambda内修改参数或

  2. 删除来自本地变量的最终标志( LambdaTranslationContext.translate(...):1894
    和参数( LambdaTranslationContext.translate(...):1899 )在lamda生成的方法中:

  1. Forbid parameter modification inside lambda or
  2. Remove FINAL flag from local variable (LambdaTranslationContext.translate(…):1894) and parameter (LambdaTranslationContext.translate(…):1899) in lamda generated method:

 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

I removed FINAL flag and got expected results on tests from: https://bugs.openjdk.java.net/browse/JDK-8038420

这篇关于Lambda的行为与匿名内部类不同的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆