ラムダ式は、Java 8 に追加された新機能です。これは、Java の関数型プログラミングの概念を導入します。では、関数型プログラミングとは何でしょうか?
関数型プログラミング: 関数型プログラミングは、式の評価として計算を記述する数学指向の抽象化です。
#私たちが通常 オブジェクト指向プログラミングと呼ぶものは、命令型プログラミングに属します。関数型プログラミングと命令型プログラミングの違いは次のとおりです:
関数型プログラミングの本質:
関数型プログラミングにおける関数は、コンピューター内の関数を指すのではなく、数学 の関数は独立変数のマッピングです。つまり、関数の値は関数パラメーターの値にのみ依存し、他の状態には依存しません。厳密な意味での関数型プログラミングとは、可変変数、代入、ループ、その他の命令型制御構造を使用しないプログラミングを意味します。 関数型プログラミングの利点: 関数型プログラミングの利点は、主に不変性によってもたらされます。可変状態がなければ、関数は参照透過的であり、副作用はありません。 上記は基本的な概念の一部ですが、これらの側面に触れることが少ないかもしれないため、最初は関数型プログラミングが珍しいものだと感じました。 簡単な例
話は安いのでコードを見せてください!
最も簡単な例から始めましょう。これは最も紹介されているものでもあります。例。ははは!ボタンにモニターを追加します。
匿名内部クラスを使用して追加します。submit.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JOptionPane.showMessageDialog(null, "点击了确定按钮", "确定", JOptionPane.INFORMATION_MESSAGE); } });
匿名内部クラス が使用できるわけではありません。これについては後述します)。
ラムダ式を使用してコードを簡素化する
submit.addActionListener((e)->{ JOptionPane.showMessageDialog(null, "点击了确定按钮", "确定", JOptionPane.INFORMATION_MESSAGE); });
ラムダ式は、データのような動作を渡す匿名メソッドです。
説明
ラムダ式を使用した簡略化されたコード表現がより明確になり、煩雑なテンプレート コードを記述する必要がないことがわかります。さらなる簡略化
コード本体のパラメータ括弧と中括弧は省略することもできます (パラメータが 1 つだけの場合、括弧は省略できます)コードが 1 行のみの場合、中括弧 は省略できます)。
ActionListener listener = e->JOptionPane.showMessageDialog(null, "点击了确定按钮", "确定", JOptionPane.INFORMATION_MESSAGE);
概要匿名内部クラスの代わりに Lambda 式を使用してオブジェクトを作成する場合、Lambda 式のコード ブロックは、抽象メソッドを実装するメソッド本体を置き換えます。 . Lambda は匿名メソッドと同等です。
return ステートメントが 1 つしかない場合は、
return を省略でき、ラムダ式は自動的にこのステートメントの値を返します。
注: 仮パラメータ リストを省略できる理由は、コンパイラが型推論を実行できるためです。例:
List<Dog> dogs1 = new ArrayList<Dog>(); List<Dog> dogs2 = new ArrayList<>();
型推論 の役割です。 ただし、型推論は全能ではなく、すべてを推論できるわけではないため、場合によっては、
仮パラメータの型を明示的に追加する必要があります。例:
BinaryOperator b = (x, y)->x*y; //上面这句代码无法通过编译,下面是报错信息:无法将 * 运算符作用于 java.lang.Object 类型。 The operator * is undefined for the argument type(s) java.lang.Object, java.lang.Object
//添加参数类型,正确的代码。 BinaryOperator<Integer> b = (x, y)->x*y;
したがって、型推論は全能ではありません。コンパイラがそれを推論できない場合、それは私たちの間違いです。コンパイラにあまり依存しないでください。場合によっては、パラメーターの型を明示的に追加する必要があることもありますが、これにはさらに練習が必要です。
関数インターフェイス前に学んだように、ラムダ式は匿名の内部クラスを置き換えてコードを簡素化し、明確に表現できます。では、ラムダ式を使用するための前提条件は何でしょうか? --関数インターフェイス
ターゲット タイプ (ターゲット タイプ) とも呼ばれるラムダ式のタイプは、関数インターフェイス (関数インターフェイス) である必要があります。いわゆる関数型インターフェイスとは次のものを指します。 抽象メソッドを 1 つだけ含むインターフェイス。 (複数のデフォルト メソッドと静的メソッドを含めることができますが、抽象メソッドは 1 つだけである必要があります)。 注: Java 8 では、特別なアノテーション
@FunctionalInterface が提供されます。インターフェイスを関数インターフェイスとしてマークするために使用され、コンパイル中にチェックされるようになります。インターフェイスに複数の抽象メソッドが含まれている場合、コンパイラはエラーを報告します。
上面使用 Lambda 表达式来为按钮添加了监视器,可以看出来,Lambda 表达式 代替了 new ActionListener()
对象。
所以 Lambda 的表达式就是被当成一个对象。
例如:
ActionListener listener = e->JOptionPane.showMessageDialog(null, "点击了确定按钮", "确定", JOptionPane.INFORMATION_MESSAGE);
从上面这个例子中可以看出来,Lambda 表达式实现的是匿名方法–因此它只能实现特定函数式接口中的唯一方法。
所以 Lambda 表达式有下面两种限制:
Lambda 表达式的目标类型必须是明确的函数式接口。 Lambda 表达式只能为函数式接口创建对象。Lambda只能实现一个方法,因此它只能为含有一个抽象方法的接口(函数式接口)创建对象。 介绍几个 Java 中重要的函数接口
从这种表可以看出来,抽象方法的名字反而不是最重要的了,重要的是参数和返回值。 因为在写 Lambda 表达式的时候,也不要使用 抽象方法名了。
上面使用几个简单的例子来说明上面接口的应用:
测试代码
import java.text.ParseException; import java.util.function.BinaryOperator; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.function.UnaryOperator; import java.util.stream.Stream; public class Test { public static void main(String[] args) throws ParseException { //Lambda 表达式中的构造器引用,用于简化代码。 Creat<Dog> c = Dog::new; Dog dog = c.creat("小黑", 15); System.out.println(dog.toString()); Predicate<String> predicate = (words)->{ return words.length() > 20; }; assert predicate.test("I love you yesterday and today!") : "长度小于20"; assert !predicate.test("God bless you!") : "长度小于20"; System.out.println("------------------------"); Consumer<Dog> consumer = System.out::println; consumer.accept(dog); System.out.println("------------------------"); Function<Dog, String> function = (dogObj)->{ return dogObj.getName(); }; System.out.println(function.apply(dog)); System.out.println("------------------------"); Supplier<Dog> supplier = ()->{ return new Dog("大黄", 4); }; System.out.println(supplier.get()); //一元操作符 UnaryOperator<Boolean> unaryOperation = (flag)->{ return !flag; }; System.out.println(unaryOperation.apply(true)); BinaryOperator<Integer> binaryOperator = (x, y)->x*y; int result = binaryOperator.apply(999, 9999); System.out.println(result); } }
测试使用的实体类
public class Dog { private String name; private int age; public Dog(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Dog [name=" + name + ", age=" + age + "]"; } }
自定义函数式接口
@FunctionalInterface public interface Creat<T> { public T creat(String name, int age); }
运行截图就不放了,感兴趣的可以试一下。
说明
我这里直接使用 Lambda 创建了对象,然后调用了这个对象的方法(就是lambda 的代码块部分),真正使用的时候,都是直接传递 Lambda 表达式的,这种方法并不推荐,但是可以让我们很好的理解为什么? 可以看出来,Lambda 表达式的作用,最后还是需要调用 重写的抽象方法的,只不过使用表达更加清晰,简化了代码。
例如:
List<Dog> dogs = new ArrayList<>(); dogs.add(new Dog("大黄", 2)); dogs.add(new Dog("小黑", 3)); dogs.add(new Dog("小哈",1)); //将行为像数据一样传递,使用集合的 forEach 方法来遍历集合, //参数可以是一个 Lambda 表达式。 Consumer<? super Dog> con = (e)->{ System.out.println(e); }; dogs.forEach(con); System.out.println("--------------------------\n"); //直接传递 Lambda 表达式,更加简洁 dogs.forEach(e->System.out.println(e)); System.out.println("--------------------------\n"); //使用方法引用,进一步简化(可以看我的另一篇关于方法引用的博客) dogs.forEach(System.out::println); System.out.println("--------------------------\n"); //使用 Lambda 对集合进行定制排序,按照年龄排序(从小到大)。 dogs.sort((e1, e2)->e1.getAge()-e2.getAge()); dogs.forEach(System.out::println);
可以看出来,通过使用 Lambda 表达式可以,极大的简化代码,更加方便的操作集合。值得一提的是:Lambda 表达式 和 Stream 的结合,可以拥有更加丰富的操作,这也是下一步学习的方向。
运行截图:
以上がJavaラムダ式の使用例分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。