当前位置: 首页 > 面试题库 >

Lambda的行为与匿名内部类不同

斜成济
2023-03-14
问题内容

在进行一些基本的lambda练习时,一个看似完全相同的匿名内部类的输出给我的输出与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));

输出 22 。这里没有新内容。

但是当我这样做时:

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

输出 23

问题:两个输出不应该相同吗? 我想念什么吗?

为了完整起见: 方案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));

输出 33 。这里也没有什么新鲜的。

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

更新#2:错误报告: https

:
//bugs.openjdk.java.net/browse/JDK-8038420

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


问题答案:

根据生成的html" target="_blank">字节码:

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;

匿名类:

  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方法不会复制参数(加载->取消装箱->添加1->框->存储->加载->返回)。

更新

绝对是javac错误。

我来自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);
}

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

然后让表达式生成器检查变量标志,以及是否在最终变量时忽略临时变量生成(根据源代码 Lower.abstractRval(…):2277
),因为认为禁止进行修改。

可能的解决方案:

  1. 禁止在lambda或
  2. 从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);

我删除了FINAL标志,并从以下网址获得了预期的测试结果:https
:
//bugs.openjdk.java.net/browse/JDK-8038420



 类似资料:
  • 下面的代码可以工作,但我收到了SonarLint的通知,因为我在流中使用了匿名类而不是lambda表达式,我不知道如何改进下面的代码来避免通知: 代码解释:我使用java.util的属性类,不幸的是,属性的返回

  • 以下代码收到Sonarint的通知: 我尝试了以下内容,但在抛出新的IllegalArgumentException时遇到了问题: 你能建议一下吗?

  • 我正在从事一个严重依赖泛型类型的项目。其关键组件之一是所谓的TypeToken,它提供了一种在运行时表示泛型类型并在其上应用一些实用函数的方法。为了避免Java的类型擦除,我使用了花括号表示法()来创建一个自动生成的子类,因为这使类型可重新定义。 这是TypeToken的一个非常简化的版本,比最初的实现要宽松得多。然而,我正在使用这种方法,因此我可以确保真正的问题不在于这些效用函数之一。 基本上,

  • 问题内容: 我想知道lambda在Java 8中有多大的好处。我同意有时使用lambda可能更具可读性,但是它对性能方面真的有很大影响吗?还是主要集中在语法糖上? 有时我更喜欢匿名内部类。 当我一直不使用lambda时,我真的会失去很多好处吗? 唯一的?大?在我看来,性能提升是因为我们实际上并没有创建类加载器必须在程序开始时加载的类,例如创建许多线程: 创建类似的类。 除此之外, 除了代码的可读性

  • 问题内容: 在此问题中用户@Holger提供了一个答案,该答案显示了匿名类的不常见用法,我并不知道。 该答案使用流,但是此问题与流无关,因为这种匿名类型构造可以在其他上下文中使用,即: 令我惊讶的是,它编译并打印了预期的输出。 注意:我很清楚,自古以来,就可以构造一个匿名内部类并按如下方式使用其成员: 但是,这不是我要问的。我的情况有所不同,因为匿名类型是通过方法链传播的。 现在,我可以想象到此功

  • 问题内容: 请看下面的代码: 在上面的代码中,在方法ModifyList()中声明的匿名内部类的实例能够访问传递给该方法的参数。AFAIK Java为内部类创建一个单独的字节码文件。 谁能解释一下Java在字节码级别上如何处理这些局部变量绑定?我的意思是,Java如何精确跟踪对作为参数传递给该方法的对象的引用? 任何帮助将不胜感激! [抱歉我的英语不好! 如果您理解我的问题,请编辑这篇文章,并删除