web-dev-qa-db-ja.com

Javaラムダ構文の内訳は何ですか?

Java 8のラムダメソッドの構文を説明してください。

whatラムダ関数の説明はたくさんありますが、構文の詳細な説明はありません。構文を正しく再現することを学ぶのは非常に難しいので、わからないなぜ彼らはそのまま書いてある。

以下は、私が遭遇する一般的なケースです。NetBeansの厚意によります。

_public static void main(String[] args) {
    SwingUtilities.invokeLater(() -> {
        new MainAppJFrame();
    });
}
_

したがって、どういうわけか、次のラムダ式は匿名のRunnableオブジェクトのrun()メソッドに解決されます。

_() -> {
    // do stuff
}
_

_->_は適切なラムダ構文ですか?中かっこは、匿名のメソッドコードを単に含んでいます。この場合、Runnable.run()メソッドを作成しているので、括弧は空の引数ですか?

これはすべて私にはかなり不明確です。コンパイラは、SwingUtilities.invokeLater(Runnable)メソッドで期待される型に基づいて、匿名のRunnableをインスタンス化することを知っていると思いますか?パラメータリストのみが異なる2つの_SwingUtilities.invokeLater_メソッドがある場合はどうなりますか?明らかにこの特定のケースにはありませんが、それは他の場所で可能です:

_interface ExampleLambdaConsumer {
    public void doSomething(Runnable r);
    public void doSomething(Java.lang.reflect.Method m);
}

class C implements ExampleLambdaConsumer {
    // implementations for doSomething methods here

    public static void main(String[] args) {
        doSomething(() -> {
            // lambda method body here
        }
    }
}
_
36
muffin

構文は次のとおりです。

_arguments -> body
_

ここで、argumentsは次のいずれかです。

  • _()_

  • その変数の型がコンテキストから推測できる場合、単一の変数

  • かっこ内の変数のシーケンス。タイプあり、またはタイプなし(またはJava 11、var以降))。
    例:_(x)_、_(x, y)_、_(int x, int y)_、_(var x, var y)_(Java 11+)。
    以下は無効です:_(int x, y)_、_(x, var y)_、_(var x, int y)_

およびbodyは、式またはステートメントを含む_{...}_ブロックのいずれかです。式(メソッドまたはコンストラクターの呼び出し以外)が単に返されます。つまり、_() -> 2_は_() -> {return 2;}_と同等です


_() -> f()_のようなラムダ式の場合(本体はメソッドまたはコンストラクター呼び出し式です):

  • f()voidを返す場合、これらは_() -> { f(); }_と同等です

  • それ以外の場合は、_() -> { f(); }_または_() -> { return f(); })_と同等です。コンパイラは呼び出しコンテキストからそれを推測しますが、通常は後者を優先します。

したがって、void handle(Supplier<T>)void handle(Runnable)の2つのメソッドがある場合、次のようになります。

  • handle(() -> { return f(); })およびhandle(() -> x)は最初のものを呼び出し、

  • handle(() -> { f(); }は2番目のものを呼び出し、

  • handle(() -> f())

    • f()voidまたはTに変換できない型を返す場合、2番目の型を呼び出します

    • f()Tに変換可能な型を返す場合、最初の型を呼び出します


コンパイラーはラムダのタイプをコンテキストに一致させようとします。正確なルールはわかりませんが、答えは次のとおりです。

パラメータリストのみが異なる2つのSwingUtilities.invokeLaterメソッドがある場合はどうなりますか?

is:それは、それらのパラメーター・リストがどうなるかによって異なります。他のinvokeLaterにもパラメータが1つだけあり、そのパラメータがvoid*()の1つのメソッドとのインターフェースでもあるタイプである場合、それは理解できないと不平を言うでしょうどの方法を意味します。

なぜ彼らはそのままに書かれているのですか?まあ、それはC#とScalaの構文がほとんど同じであるためです(これらは_=>_ではなく_->_を使用しています)。

44
Karol S

構文は

(parameter_list_here) -> { stuff_to_do; }

中かっこは、単一の式の場合は省略できます。パラメータリストが単一のパラメータの場合は、パラメータリストを囲む通常の括弧を省略できます。

構文は、すべての機能インターフェースでのみ機能します。 @FunctionalInterfaceアノテーションは、そのようなインターフェースを作成するつもりであることをコンパイラーに伝え、要件を満たしていない場合はコンパイルエラーを出します-たとえば、オーバーライド可能なメソッドが1つだけ必要です。

@FunctionalInterface
interface TestInterface {
    void dostuff();
}

Runnableもそのように宣言されています。他のインターフェイスはそうではなく、ラムダ関数と一緒に使用することはできません。

パラメータを取らないメソッドで新しい機能インターフェースを作成したので、シグネチャの「衝突」に関する質問をテストしてみませんか?

public class Main {
    private void test(Runnable r) {

    }
    private void test(TestInterface ti) {

    }
    public static void main(String[] args) { 
        test(() -> { System.out.println("test");})
    }

    @FunctionalInterface
    interface TestInterface {
        void dostuff();
    }
}

結果:コンパイルエラー:メソッドtestへのあいまいな呼び出し。

ご覧のとおり、コンパイラ/ VM(ランタイムを実行した場合)は適切なメソッドとそのパラメーターリストを見つけ、パラメーターが機能的なインターフェイスであるかどうか、そしてそれがそのインターフェイスの匿名の実装を作成するかどうかを確認します。技術的には(バイトコードで)匿名クラスとは異なりますが、それ以外は同じです(Main $ 1.classファイルは表示されません)。

サンプルコード(Netbeansの好意による)は、

SwingUtilities.invokeLater(MainAppJFrame::new);

ところで:)

10
Xabster

ラムダ式は基本的にJava 8 to 簡素化するプロセス関数を匿名関数として)で採用されています。

それらはオーバーライド古いJava匿名関数への単なるショートカットです。

次の例を参照してください。

以下のように宣言されたメソッドが1つだけあるinterface Aがあるとします。

interface A{        
    void print();           
}

old Java styleを使用して、overrideこれを匿名で以下のように記述します。

new A() {           
        @Override
        public void print() {
            System.out.println("in a print method");                
        }           
};

さらにJava 8ラムダ式を使用して、以下のように使用します。

() -> System.out.println("in a print method");

ここでは、->演算子の前にメソッドに必要なパラメーターを渡し、->演算子の後に本体をオーバーライドできます。

これを実現するために必要な唯一の設定は、以下のように@ FunctionalInterfaceでインターフェイスを宣言する必要があることです。

 @FunctionalInterface
 interface A{        
    void print();           
 }

注:-ラムダ式は、デフォルト以外のメソッドが1つしかない「機能的な」インターフェースにのみ使用できます。

8
Pramod S. Nikam

構文は紛らわしいです。それはひねりの周りに私を駆動します。それがIntellijでなかった場合、私を訂正すると私はそれを誤解してしまいます。

OKコンパイルします。

class Scratch {
public static void main(String[] args) {
    Predicate<Integer> even = integer -> {return (integer%2 == 0);};
}

}

OKコンパイルします。

class Scratch {
public static void main(String[] args) {
    Predicate<Integer> even = integer -> {return integer%2 == 0;};
}

}

ステートメントではなくなったため、コンパイルしません。

class Scratch {
public static void main(String[] args) {
    Predicate<Integer> even = integer -> {return integer%2 == 0};
}

}

コンパイルされません。これはステートメントではなくなり、returnステートメントが削除されたためです。

class Scratch {
public static void main(String[] args) {
    Predicate<Integer> even = integer -> {integer%2 == 0};
}

}

OKコンパイルします。

class Scratch {
public static void main(String[] args) {
    Predicate<Integer> even = integer -> integer%2 == 0;
}

}

したがって、ストーリーのモラルは、ブラケットを使用する場合、内部にあるものはステートメントである必要があり、Functionalインターフェースの単一の抽象メソッド(つまり、デフォルトではない唯一のメソッド)が予期するタイプを返す必要があります。

実際、私は他の何よりも自分のためにそれを書いた。

0
Beezer