ホームページ >Java >&#&チュートリアル >Java Lambda式の詳しい説明とサンプルコード
Java ラムダ式は、Java 8 で導入された新機能です。これは、関数型プログラミングをシミュレートするための構文糖衣と言えますが、JavaScript のクロージャに似ていますが、主な目的は、Simplify に関数型構文を提供することです。私たちのコーディング。
Lambdaの基本構文
Lambdaの基本的な構造は、(arguments)->bodyとなります。
パラメータの型が推定できる場合、(など)の型を指定する必要はありません。 a) -> System.out.println(a)
パラメータが 1 つしかなく、型が推定できる場合は、->out.println(a) のように () を記述する必要はありません。 )
パラメータで型を指定する場合は、(int a ) -> System.out.println(a) のように括弧が必要です
パラメータは () -> のように空にすることもできます。 .println("hello")
bodyにはステートメントが1つしかない場合は{}を使ってステートメントを含める必要があります
一般的な書き方は以下の通りです
(a) -> a * a
(int a, int b) -> a + b
(a, b) -> {return a - b;}
() -> System.out.println() .getId())
FunctionalInterfaceFunctionalInterface
Concept
Java Lambda 式は関数インターフェイスに基づいています。関数型インターフェース(FunctionalInterface)とは何ですか? 簡単に言えば、このタイプのインターフェイスの目的は 1 つのメソッド (関数) のみであり、1 つの関数に相当します。 Runnable や Comparator などの一般的なインターフェイスは関数インターフェイスであり、@FunctionalInterface の注釈が付けられます。
例
理解しやすいように、スレッドを例として取り上げます。 Runnable インターフェイスは、スレッド プログラミングで一般的に使用されるインターフェイスであり、スレッドの実行ロジックである void run() メソッドが含まれています。前の構文によると、新しいスレッドを作成するときは、次のように Runnable 匿名クラスを使用することが一般的です。
new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getId()); } }).start();
あまり書きすぎると退屈になりますが、Lambda ベースの記述方法は簡潔になり、次のようにクリアします:
new Thread(() -> System.out.println(Thread.currentThread().getId())).start();
Thread のパラメータに注意してください。 Runnable の実装は、以下のように書かれた一文で実現されます
Runnable r = () -> System.out.println(Thread.currentThread().getId());
new Thread(r).start( );
Lambdaの目的はもちろん簡単に書けるだけでなく、高次の目的も気づいたらまとめられます。
コンパレータの別の例を見てください。従来の記述方法によれば、次のようになります。
Integer[] a = {1, 8, 3, 9, 2, 0, 5}; Arrays.sort(a, new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o1 - o2; } });
ラムダ式は次のように記述されます:
Integer[] a = {1, 8, 3, 9, 2, 0, 5} ;
Arrays.sort(a, (o1, o2) -> o1 - o2);
JDKの関数インターフェース
既存のクラスライブラリがラムダ式を直接使用するために、いくつかのものが存在しましたJava 8 より前 インターフェースは関数インターフェースとしてマークされています:
java.lang.Runnable
java.util.Comparator
java.util.concurrent.Callable
java.io.FileFilter
java.security.PrivilegedAction
java .beans.PropertyChangeListener
Java 8 では、一般的に使用される関数インターフェイスを提供する新しいパッケージ java.util.function が追加されました。 , U, R> - 関数: 入力 T と U と出力 R オブジェクト
- アサーション/判定: 入力 T と出力 boolean
BiPredicateabbd655bd3f9f929be0207abcc18a2ef - アサーション/判定: 入力 T と U 出力boolean
Supplier8742468051c85b06f0a0af9e3e506b5c - プロデューサー: 入力なし、出力 T
Consumer8742468051c85b06f0a0af9e3e506b5c - コンシューマー: 入力 T、出力なし
BiConsumerabbd655bd3f9f929be0207abcc18a2ef - コンシューマー: 入力 T および U 出力なし
UnaryOperatorb32f285eaba7a6752dff0bc229700674 - 単位演算: 入力 T、出力 T
BinaryOperator8742468051c85b06f0a0af9e3e506b5c - バイナリ演算: 入力 T と T、出力 T
さらに、以下を含む、より具体的な関数とインターフェイスが追加されます。 DoubleBinaryOperator、DoubleConsumer、DoubleFunction0f1763a9dfcc95d54eac98034f0cdcdd、DoublePredicate、DoubleSupplier、DoubleToIntFunction、DoubleToLongFunction、DoubleUnaryOperator、IntBinaryOperator、IntConsumer、IntFunction0f1763a9dfcc95d54eac98034f0cdcdd、IntPredicate、IntSupplier、IntToDoubleFunction、IntToLongFunction、IntUnary 演算子、LongBinaryOperator、 LongFunction0f1763a9dfcc95d54eac98034f0cdcdd、LongPredicate、 LongSupplier、LongToDoubleFunction、LongToIntFunction、LongUnaryOperator、ToDoubleBiFunction1a3683039eb4273de3fd0b6e58753f8a、ToDoubleFunction8742468051c85b06f0a0af9e3e506b5c、ToIntBiFunction1a3683039eb4273de3fd0b6e58753f8a、ToIntFunction8742468051c85b06f0a0af9e3e506b5c、ToLongBiFunction1a3683039eb4273de3fd0b6e58753f8a、ToL ongFunction8742468051c85b06f0a0af9e3e506b5c上記の関数型インターフェイスと組み合わせると、これらの基本的なタイプの関数型インターフェイスの機能がクラス名から一目でわかります。
関数型インターフェイスを作成する
場合によっては、関数型インターフェイスを自分で実装する必要があります。この方法も非常に簡単です。まず、このインターフェイスに関数操作が 1 つだけ含まれていることを確認し、そのインターフェイスに @FunctionalInterface の注釈を付ける必要があります。タイプ。
型導出
型導出はラムダ式の基礎であり、型導出のプロセスはラムダ式のコンパイルプロセスです。次のコードを例として挙げます:
Function00c20620d278363633dd30e58ef30cbd strToInt = str -> Integer.parseInt(str);
编译期间,我理解的类型推导的过程如下:
先确定目标类型 Function
Function 作为函数式接口,其方法签名为:Integer apply(String t)
检测 str -> Integer.parseInt(str) 是否与方法签名匹配(方法的参数类型、个数、顺序 和返回值类型)
如果不匹配,则报编译错误
这里的目标类型是关键,通过目标类型获取方法签名,然后和 Lambda 表达式做出对比。
方法引用
方法引用(Method Reference)的基础同样是函数式接口,可以直接作为函数式接口的实现,与 Lambda 表达式有相同的作用,同样依赖于类型推导。方法引用可以看作是只调用一个方法的 Lambda 表达式的简化。
方法引用的语法为: Type::methodName 或者 instanceName::methodName , 构造函数对应的 methodName 为 new。
例如上面曾用到例子:
Function00c20620d278363633dd30e58ef30cbd strToInt = str -> Integer.parseInt(str);
对应的方法引用的写法为
Function00c20620d278363633dd30e58ef30cbd strToInt = Integer::parseInt;
根据方法的类型,方法引用主要分为一下几种类型,构造方法引用、静态方法引用、实例上实例方法引用、类型上实例方法引用等
构造方法引用
语法为: Type::new 。 如下面的函数为了将字符串转为数组
方法引用写法
Function00c20620d278363633dd30e58ef30cbd strToInt = Integer::new;
Lambda 写法
Function00c20620d278363633dd30e58ef30cbd strToInt = str -> new Integer(str);
传统写法
Function<String, Integer> strToInt = new Function<String, Integer>() { @Override public Integer apply(String str) { return new Integer(str); } };
数组构造方法引用
语法为: Type[]::new 。如下面的函数为了构造一个指定长度的字符串数组
方法引用写法
Function440e1640c9faa3393c37ba0de3f32bfa fixedArray = String[]::new;
方法引用写法
Function440e1640c9faa3393c37ba0de3f32bfa fixedArray = length -> new String[length];
传统写法
Function<Integer, String[]> fixedArray = new Function<Integer, String[]>() { @Override public String[] apply(Integer length) { return new String[length]; } };
静态方法引用
语法为: Type::new 。 如下面的函数同样为了将字符串转为数组
方法引用写法
Function00c20620d278363633dd30e58ef30cbd strToInt = Integer::parseInt;
Lambda 写法
Function00c20620d278363633dd30e58ef30cbd strToInt = str -> Integer.parseInt(str);
传统写法
Function<String, Integer> strToInt = new Function<String, Integer>() { @Override public Integer apply(String str) { return Integer.parseInt(str); } };
实例上实例方法引用
语法为: instanceName::methodName 。如下面的判断函数用来判断给定的姓名是否在列表中存在
Listf7e83be87db5cd2d9a8a0b8117b38cd4 names = Arrays.asList(new String[]{"张三", "李四", "王五"});
Predicatef7e83be87db5cd2d9a8a0b8117b38cd4 checkNameExists = names::contains;
System.out.println(checkNameExists.test("张三"));
System.out.println(checkNameExists.test("张四"));
类型上实例方法引用
语法为: Type::methodName 。运行时引用是指上下文中的对象,如下面的函数来返回字符串的长度
Function<String, Integer> calcStrLength = String::length; System.out.println(calcStrLength.apply("张三")); List<String> names = Arrays.asList(new String[]{"zhangsan", "lisi", "wangwu"}); names.stream().map(String::length).forEach(System.out::println);<br>
又比如下面的函数已指定的分隔符分割字符串为数组
BiFunction505cb6255f356d4ffe44ba9665547740 split = String::split;
String[] names = split.apply("zhangsan,lisi,wangwu", ",");
System.out.println(Arrays.toString(names));
Stream 对象
概念
什么是 Stream ? 这里的 Stream 不同于 io 中的 InputStream 和 OutputStream,Stream 位于包 java.util.stream 中, 也是 java 8 新加入的,Stream 只的是一组支持串行并行聚合操作的元素,可以理解为集合或者迭代器的增强版。什么是聚合操作?简单举例来说常见的有平均值、最大值、最小值、总和、排序、过滤等。
Stream 的几个特征:
单次处理。一次处理结束后,当前Stream就关闭了。
支持并行操作
常见的获取 Stream 的方式
从集合中获取
Collection.stream();
Collection.parallelStream();
静态工厂
Arrays.stream(array)
Stream.of(T …)
IntStream.range()
这里只对 Stream 做简单的介绍,下面会有具体的应用。要说 Stream 与 Lambda 表达式有什么关系,其实并没有什么特别紧密的关系,只是 Lambda 表达式极大的方便了 Stream 的使用。如果没有 Lambda 表达式,使用 Stream 的过程中会产生大量的匿名类,非常别扭。
举例
以下的demo依赖于 Employee 对象,以及由 Employee 对象组成的 List 对象。
public class Employee { private String name; private String sex; private int age; public Employee(String name, String sex, int age) { super(); this.name = name; this.sex = sex; this.age = age; } public String getName() { return name; } public String getSex() { return sex; } public int getAge() { return age; } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("Employee {name=").append(name).append(", sex=").append(sex).append(", age=").append(age) .append("}"); return builder.toString(); } } List<Employee> employees = new ArrayList<>(); employees.add(new Employee("张三", "男", 25)); employees.add(new Employee("李四", "女", 24)); employees.add(new Employee("王五", "女", 23)); employees.add(new Employee("周六", "男", 22)); employees.add(new Employee("孙七", "女", 21)); employees.add(new Employee("刘八", "男", 20));
打印所有员工
Collection 提供了 forEach 方法,供我们逐个操作单个对象。
employees.forEach(e -> System.out.println(e));
或者
employees.stream().forEach(e -> System.out.println(e));
按年龄排序
Collections.sort(employees, (e1, e2) -> e1.getAge() - e2.getAge());
employees.forEach(e -> System.out.println(e));
或者
employees.stream().sorted((e1, e2) -> e1.getAge() - e2.getAge()).forEach(e -> System.out.println(e));
打印年龄最大的女员工
max/min 返回指定排序条件下最大/最小的元素
Employee maxAgeFemaleEmployee = employees.stream() .filter(e -> "女".equals(e.getSex())) .max((e1, e2) -> e1.getAge() - e2.getAge()) .get(); System.out.println(maxAgeFemaleEmployee);
打印出年龄大于20 的男员工
filter 可以过滤出符合条件的元素
employees.stream()
.filter(e -> e.getAge() > 20 && "男".equals(e.getSex()))
.forEach(e -> System.out.println(e));
打印出年龄最大的2名男员工
limit 方法截取有限的元素
employees.stream() .filter(e -> "男".equals(e.getSex())) .sorted((e1, e2) -> e2.getAge() - e1.getAge()) .limit(2) .forEach(e -> System.out.println(e));
打印出所有男员工的姓名,使用 , 分隔
map 将 Stream 中所有元素的执行给定的函数后返回值组成新的 Stream
String maleEmployeesNames = employees.stream() .map(e -> e.getName()) .collect(Collectors.joining(",")); System.out.println(maleEmployeesNames);
统计信息
IntSummaryStatistics, DoubleSummaryStatistics, LongSummaryStatistics 包含了 Stream 中的汇总数据。
IntSummaryStatistics stat = employees.stream() .mapToInt(Employee::getAge).summaryStatistics(); System.out.println("员工总数:" + stat.getCount()); System.out.println("最高年龄:" + stat.getMax()); System.out.println("最小年龄:" + stat.getMin()); System.out.println("平均年龄:" + stat.getAverage());
总结
Lambda 表达式确实可以减少很多代码,能提高生产力,当然也有弊端,就是复杂的表达式可读性会比较差,也可能是还不是很习惯的缘故吧,如果习惯了,相信会喜欢上的。凡事都有两面性,就看我们如何去平衡这其中的利弊了,尤其是在一个团队中。
以上就是对Java8 JavaLambda 的资料整理,后续继续补充相关资料谢谢大家对本站的支持!
更多Java Lambda 表达式详解及示例代码相关文章请关注PHP中文网!