Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/348.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Lambda的行为与匿名内部类不同_Java_Lambda_Java 8_Anonymous Inner Class - Fatal编程技术网

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

Java Lambda的行为与匿名内部类不同,java,lambda,java-8,anonymous-inner-class,Java,Lambda,Java 8,Anonymous Inner Class,在做一些基本的lambda练习时,一个显然相同的匿名内部类的输出给了我一个与lambda不同的输出 interface Supplier<T> { T get(T t); } 接口供应商{ T得到(T); } 情景#1 Supplier<Integer> s1 = new Supplier<Integer>() { @Override public Integer get(Integer t) { return t

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

interface Supplier<T> {

    T get(T t);
}
接口供应商{
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));
供应商s1=新供应商(){
@凌驾
公共整数get(整数t){
返回t;
}
};
供应商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));
供应商s1=新供应商(){
@凌驾
公共整数get(整数t){
返回t++;
}
};
供应商s2=t->t++;
System.out.println(s1.get(2));
System.out.println(s2.get(2));
输出23

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

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));
供应商s1=新供应商(){
@凌驾
公共整数get(整数t){
return++t;
}
};
供应商s2=t->++t;
System.out.println(s1.get(2));
System.out.println(s2.get(2));
输出33。这里也没什么新鲜事

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

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:该错误已在javac中修复,您现在应该能够获得相同的结果。

根据生成的字节码:

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;
匿名类:

 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错误

我从你那里得到消息来源

匿名类和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,因为lambdatamethod转换器 将所有参数标记为最终参数(根据源代码lambdataranslationcontext.translate(…):1899

然后让ExpressionBuilder检查变量标志,如果是final,则忽略临时变量生成 (根据源代码Lower.abstractRval(…):2277),因为修改被认为是被禁止的

可能的解决办法:

  • 禁止在lambda或
  • 从局部变量(lambdataranslationcontext.translate(…):1894)中删除最终标志 和lamda生成方法中的参数(lambdataranslationcontext.translate(…):1899):

     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标志,并从中获得了预期的测试结果:

    对于您的第二个代码段,我得到了2和2。对于cannot replicateWell,我得到了+1,这很有趣。我们在这里看到的是与JVM版本/构建相关的东西吗?我正在使用build 1.8.0-ea-b81。正在下载较新版本。将发布更新。我在MacOS X Mavericks上复制了这个bug。
    java-version
    :java version“1.8.0”java(TM)SE运行时环境(构建1.8.0-b132)java热点(TM)64位服务器虚拟机(构建25.0-b70,混合模式)我可以用
    java(TM)SE运行时环境(构建1.8.0-ea-b118)重现它,但无论JRE8Eclipse在使用什么,我都不会这样做。尽管目前还没有关于这个问题的官方说法,但我接受这一正确答案。我想我应该提交一份bug报告。有人能告诉我怎么做吗OpenJDK lambda dev邮件列表是的,这看起来像javac中的一个bug。幸运的是,Eclipse的Java编译器没有这个bug。我已经在lambda开发人员列表上发布了这个问题:我已经在这里用我的消息副本更新了我的答案: