ラムダは匿名の内部クラスとは異なる動作をします

victorantunes:

基本的なラムダ演習を行っている間、明らかに同一の匿名の内部クラスからの出力はラムダとは異なる出力を与えていました。

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ここには何も新しいものはありません。


しかし、これを行うと:

シナリオ#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ここにも新しいことはありません。

更新:1.8.0-b132から同じ出力を取得

アップデート#2:バグレポート: https : //bugs.openjdk.java.net/browse/JDK-8038420

アップデート#3:バグはjavacで修正されました。これで同じ結果が得られるはずです。

イヴァンババニン:

生成されたバイトコードによると:

Java(TM)SEランタイム環境(ビルド1.8.0-b132)

ラムダ:

 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からソースを取得しました

匿名クラスとラムダは、次の中間表現に変換されます。

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

LambdaToMethodトランスレーターはすべてのパラメーターをFINALとしてマークするため(ソースコードLambdaTranslationContext.translate(…):1899によると、ラムダでは、最終としてマークされたメソッドパラメーターがマークされます

次に、式ビルダーが変数フラグをチェックし、最終的なものである場合は一時変数の生成を省略します(ソースコードLower.abstractRval(…):2277によると)。これは、変更が禁止されていると見なされているためです。

可能な解決策:

  1. ラムダ内のパラメータ変更を禁止するか
  2. ローカル変数(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

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

分類Dev

Javaラムダには、匿名の内部クラスとは異なる変数要件があります。

分類Dev

ジェネリックラムダとジェネリック関数は異なる動作をします

分類Dev

匿名クラスをラムダに変換した後、コードの動作が異なる

分類Dev

TypeScriptクラスの継承はES6の同等のものとは異なる動作をします

分類Dev

ngClassは、cdk-drag-dropを使用した通常のクラスとは異なる動作をします

分類Dev

__setメソッドと__getメソッドはクラスの内部と外部で異なる動作をすることができますか?

分類Dev

匿名内部クラスと比較して、Kotlinラムダはどのように機能しますか?

分類Dev

なぜJavaは内部的に静的クラスとして匿名クラスを作成するのですか?

分類Dev

Lambda式がKotlinクラスとJavaクラスで異なる動作をするのはなぜですか?

分類Dev

ランダムは、デバッグの開始または「ステップイン/オーバー」に応じて異なる動作をします

分類Dev

匿名型宣言は、 "var"と ":="を介して異なる動作をします

分類Dev

Kotlinはラムダの内部クラスを生成します

分類Dev

sonarLint:この匿名の内部クラスをラムダにします

分類Dev

[SonarLint]:この匿名の内部クラスをラムダにします

分類Dev

揮発性クラス型の破棄された値式は、揮発性組み込み型の値式とは異なる動作をします

分類Dev

Google Apps Scriptでは、新しいV8ランタイムを使用すると、ブレークポイントの動作が異なりますか?

分類Dev

Tensorflowcondはソースとは異なる動作をします

分類Dev

別の一般的な動作の代わりに明示的な匿名内部クラスのラムダを使用した場合

分類Dev

C ++ 0x-ラムダ式はJavaの匿名内部クラスと同じように見えますか?

分類Dev

アクションとコントローラーで異なる認証スキームを使用したAuthorize属性の動作は何ですか

分類Dev

pyzmqサブスクライバーがasyncioと異なる動作をするのはなぜですか?

分類Dev

zshのIFSはbashとは異なる動作をします

分類Dev

Reddit json apiは、ブラウザーとnodejsリクエストを介してアクセスすると異なる動作をします

分類Dev

スライスパラメータの再割り当ては異なる動作をします

分類Dev

匿名の内部クラスをLambdaに置き換えます。これはどのように作動しますか?

分類Dev

isEnum()匿名内部クラスのあいまいな動作

分類Dev

strまたはintをサブクラス化することは、リストまたはdictをサブクラス化することとは異なる動作をするのはなぜですか?

分類Dev

Java Httpsクライアント証明書は、ブラウザや他のツールとはまったく異なる動作をします

分類Dev

Java Httpsクライアント証明書は、ブラウザや他のツールとはまったく異なる動作をします

Related 関連記事

  1. 1

    Javaラムダには、匿名の内部クラスとは異なる変数要件があります。

  2. 2

    ジェネリックラムダとジェネリック関数は異なる動作をします

  3. 3

    匿名クラスをラムダに変換した後、コードの動作が異なる

  4. 4

    TypeScriptクラスの継承はES6の同等のものとは異なる動作をします

  5. 5

    ngClassは、cdk-drag-dropを使用した通常のクラスとは異なる動作をします

  6. 6

    __setメソッドと__getメソッドはクラスの内部と外部で異なる動作をすることができますか?

  7. 7

    匿名内部クラスと比較して、Kotlinラムダはどのように機能しますか?

  8. 8

    なぜJavaは内部的に静的クラスとして匿名クラスを作成するのですか?

  9. 9

    Lambda式がKotlinクラスとJavaクラスで異なる動作をするのはなぜですか?

  10. 10

    ランダムは、デバッグの開始または「ステップイン/オーバー」に応じて異なる動作をします

  11. 11

    匿名型宣言は、 "var"と ":="を介して異なる動作をします

  12. 12

    Kotlinはラムダの内部クラスを生成します

  13. 13

    sonarLint:この匿名の内部クラスをラムダにします

  14. 14

    [SonarLint]:この匿名の内部クラスをラムダにします

  15. 15

    揮発性クラス型の破棄された値式は、揮発性組み込み型の値式とは異なる動作をします

  16. 16

    Google Apps Scriptでは、新しいV8ランタイムを使用すると、ブレークポイントの動作が異なりますか?

  17. 17

    Tensorflowcondはソースとは異なる動作をします

  18. 18

    別の一般的な動作の代わりに明示的な匿名内部クラスのラムダを使用した場合

  19. 19

    C ++ 0x-ラムダ式はJavaの匿名内部クラスと同じように見えますか?

  20. 20

    アクションとコントローラーで異なる認証スキームを使用したAuthorize属性の動作は何ですか

  21. 21

    pyzmqサブスクライバーがasyncioと異なる動作をするのはなぜですか?

  22. 22

    zshのIFSはbashとは異なる動作をします

  23. 23

    Reddit json apiは、ブラウザーとnodejsリクエストを介してアクセスすると異なる動作をします

  24. 24

    スライスパラメータの再割り当ては異なる動作をします

  25. 25

    匿名の内部クラスをLambdaに置き換えます。これはどのように作動しますか?

  26. 26

    isEnum()匿名内部クラスのあいまいな動作

  27. 27

    strまたはintをサブクラス化することは、リストまたはdictをサブクラス化することとは異なる動作をするのはなぜですか?

  28. 28

    Java Httpsクライアント証明書は、ブラウザや他のツールとはまったく異なる動作をします

  29. 29

    Java Httpsクライアント証明書は、ブラウザや他のツールとはまったく異なる動作をします

ホットタグ

アーカイブ