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
}
}
}
_
構文は次のとおりです。
_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の構文がほとんど同じであるためです(これらは_=>
_ではなく_->
_を使用しています)。
構文は
(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);
ところで:)
ラムダ式は基本的に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つしかない「機能的な」インターフェースにのみ使用できます。
構文は紛らわしいです。それはひねりの周りに私を駆動します。それが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インターフェースの単一の抽象メソッド(つまり、デフォルトではない唯一のメソッド)が予期するタイプを返す必要があります。
実際、私は他の何よりも自分のためにそれを書いた。