Lambda表达式

    • 范例:观察传统开发中的问题
    • 范例:使用Lambda表达式实现与之前完全一样的功能
    • Lambda表达式的几种格式
    • 使用Lambda表达式(无参)
    • 使用Lambda表达式(有参)
    • 使用Lambda表达式简化(再度简化Lambda表达式,把return语句也省略)
    • Lambda 表达式使用总结
    • 练习
  • 方法引用
    • 方法引用的三种形式
    • 类引用静态方法
    • 对象引用非静态方法
    • 类引用普通方法
    • 构造引用
  • JDK8自带函数式接口
    • 功能型函数式接口
    • 消费型函数式接口
    • 供给型函数式接口
    • 断言型函数式接口
  • 总结

​ 从JDK1.8开始为了简化使用者进行代码的开发,专门提供有Lambda表达式的支持,利用此操作可以实现函数式的编程,对于函数式编程比较著名的语言有:Haskell、Scala,利用函数式的编程可以避免掉面向对象编程之中一些繁琐的处理问题。

范例:观察传统开发中的问题

interface IMessage {public void send(String str);
}public class JavaDemo {public static void main(String[] args) {IMessage i = new IMessage() {public void send(String str) { System.out.println(str);}};i.send("www.baidu.com");}
}

在这样的程序里,实际上核心的功能只有一行语句System.out.println(str),但是为了这一行的核心语句,我们仍然需要按照完整的面向对象给出的结构进行开发。于是这些问题随着技术的发展也是越来越突出了。

范例:使用Lambda表达式实现与之前完全一样的功能

lambda 表达式的形式为 () -> {}; () 里面是函数式接口中抽象方法的形参, {} 中是抽象方法的实现。

interface IMessage {public void send(String str);
}public class JavaDemo {public static void main(String[] args) {IMessage i = (str) -> {System.out.println(str);};i.send("www.baidu.com");}
}

现在在代码中就十分简洁了,只写了关键语句System.out.println(str) ,于是利用这种形式就避免了面向对象复杂的结构化要求。这便是Lambda表达式的基本处理形式。

Lambda表达式如果要想使用,那么必须要有一个重要的实现要求:SAM(Single Abstract Method),接口中只有一个抽象方法。以IMessage接口为例,这个接口里面只是提供有一个send()方法,除此之外没有任何其他方法定义,所以这样的接口就被称为函数式接口,而只有函数式接口才能被Lambda表达式所使用。

范例:错误的定义

interface IMessage {public void send(String str);public void say();
}public class JavaDemo {public static void main(String[] args) {IMessage i = (str) -> {System.out.println(str);};i.send("www.baidu.com");}
}

所以很多时候为了明确的标注出你是一个函数式接口,往往会在接口上面增加一行注释@functionalInterface。但是,默认方法和静态方法不会破坏函数式接口的定义。

之所以在JDK1.8之后提供有默认和静态方法,也都是为函数式开发做准备。

Lambda表达式的几种格式

  • 方法没有参数: () -> {};

  • 方法有参数::(参数,…,参数) -> {};

下面看几个例子:

使用Lambda表达式(无参)

要创建接口 IMessage 的实现类,如果该类只是使用一次,我们可以使用匿名内部类的方式,但是匿名内部类写起来很麻烦。而 IMessage 接口中,只有一个抽象方法,是一个函数式接口,那么我们就可以使用 lambda 来代替匿名内部类。lambda 体就是接口的实现。

@FunctionalInterface
interface IMessage {public void send();
}public class JavaDemo {public static void main(String[] args) {IMessage i = () -> {System.out.println("www.baidu.com");};i.send();}
}

上述写法还是有点麻烦,在 -> 右边的方法体中,如果只有一行语句,那么 可以省略大括号,直接一行搞定。

@FunctionalInterface
interface IMessage {public void send();
}public class JavaDemo {public static void main(String[] args) {IMessage i = () -> System.out.println("www.baidu.com");i.send();}
}

注:抽象方法如果没有参数,则 lambda 表达式不能省略 ();

使用Lambda表达式(有参)

IMath 接口中只有一个抽象方法,该方法有返回值,且有两个参数。可以使用 lambda 进行简化。

@FunctionalInterface
interface IMath {public int add(int x, int y);
}public class JavaDemo {public static void main(String[] args) {// t1,t2是形参名,随便取,但是个数必须匹配形参IMath math = (t1, t2) -> { return t1 + t2;};System.out.println(math.add(20, 30));}
}

以上的表达式之中你会发现只有一行语句“ return t1 + t2;”,这时候可以进一步简化。

使用Lambda表达式简化(再度简化Lambda表达式,把return语句也省略)

@FunctionalInterface
interface IMath{public int add(int x , int y);
}
public class Demo01 {IMath math = (n1,n2)-> n1 + n2;System.out.println(math.add(10,20));}}

利用Lambda表达式确实可以使得代码更加简便。

Lambda 表达式使用总结

  • lambda 表达式形式为 ()->{},-> 左边是抽象方法的形参列表, -> 是抽象方法的实现体。
  • lambda 方法如果没有参数或有两个及以上的参数,则 小括号不能省略
  • lambda 方法如果只有一个参数,则小括号可以省略
  • lambda 方法体如果只有一行语句,则 大括号和return都可省略。
  • 省略了大括号,则必须省略 return,省略了 return ,则必须省略 {},这俩要么成对出现,要么都不出现。

lambda 的本质就是函数式接口的一个实现类。

练习

Java中Comparator 接口使得开发人员可以定制排序。在 Arrays 的 sort 方法中,就可以传入一个 Comparator 接口,进行定制排序。那么我们就可以使用 lambda 接口来简化 Comparator 的实现。

匿名内部类原始写法如下

 public void test5() {Comparator<Integer> com=new Comparator<Integer>(){@Overridepublic int compare(Integer o1, Integer o2) {return Integer.compare(o1,o2);}};}

lambda 简化
compare 方法需要两个参数,返回一个 int 类型的值。那么 () 中两个参数,return Integer重写的比较方法即可。

 public void test5() {Comparator<Integer> com= (o1,o2)->{return Integer.compare(o1,o2);};}

还可以使用 方法引用 进一步简化:

 public void test5() {Comparator<Integer> com= Integer::compare;}

方法引用

方法引用的三种形式

  • 对象 :: 非静态方法

  • 类 :: 静态方法
  • 类 :: 非静态方法

注:方法引用规定,对象不能调用静态方法,这和面向对象的思想一致。但类可以调用非静态方法,这是面向对象中不允许的。

类引用静态方法

语法格式: 类名称::static方法名称

第一步,我们自定义一个接口,该接口中只有一个抽象方法,是一个函数式接口。

第二步,随便建立一个类,创建一个方法。这里要注意,创建的方法返回值类型和形参列表必须和函数式接口中的抽象方法相同。

第三步,创建函数式接口的实现类,我们可以使用方法引用。相当于实现类里的重写的方法,就是方法引用的方法。这样才能方法引用。

@FunctionalInterface
interface IMessage<T,P> {public T transfer(P p);
}class Supplier{public static String getStr(Integer integer) {return String.valueOf(integer);}
}public class JavaDemo {public static void main(String[] args) {IMessage<String, Integer> msg = Supplier::getStr;System.out.println(msg.transfer(31415926));}
}

对象引用非静态方法

语法格式: 实例化对象::普通方法;

有了类引用静态方法的基础,相信大家已经有了一点感觉。

对象引用非静态方法,和类引用静态方法一致。要求我们对象引用的方法,返回值和形参列表要和函数式接口中的抽象方法相同。

@FunctionalInterface
interface IMessage {public double get();
}class Supplier{private Double salary;public Supplier() {}public Supplier(Double salary){this.salary = salary;}public Double getSalary() {return this.salary;}
}public class JavaDemo {public static void main(String[] args) {Supplier supplier = new Supplier(9999.9);IMessage msg = supplier::getSalary;System.out.println(msg.get());}
}

类引用普通方法

语法格式: 类::普通方法

类引用普通方法就有点难以理解了。

当抽象方法中有两个参数,且第一个参数是调用者,第二个参数是形参,则可以使用类::实例方法。

@FunctionalInterface
interface IMessage<T, P> {// 要看成 T res = p1.compare(p2);public T compare(P p1, P p2);
}@Data
class Person {public Person() {}public Person(String name, Integer age) {this.name = name;this.age = age;}private String name;private Integer age;public boolean equal(Person per) {return this.name.equals(per.getName()) && this.age.equals(per.getAge());}
}public class JavaDemo {public static void main(String[] args) {Person person1 = new Person("张三", 22);Person person2 = new Person("张三", 23);// 符合T res = p1.compare(p2);IMessage<Boolean, Person> msg = Person::equal;System.out.println(msg.compare(person1, person2));}
}

再来一个例子:

@FunctionalInterface
interface IMessage<T, P, V> {// 看成 T res = p1.compare(p2);// public int compareTo(String anotherString){} 符合抽象方法的格式// int res = str1.compare(str2);public T compare(P p1, V p2);
}public class JavaDemo {public static void main(String[] args) {IMessage<Integer,String,String> stringCompare = String::compareTo;Integer compare = stringCompare.compare("adc", "abd");System.out.println(compare);}
}

刚开始可能不适应,需要慢慢体会。

构造引用

语法格式: 类名称::new

@FunctionalInterface
interface IMessage<T, P, V> {//  public T create(P p1, V p2); 符合抽象方法的要求public T create(P p1, V p2);
}@Data
class Person {public Person() {}public Person(String name) {this.name = name;}// 符合 public T create(P p1, V p2); 要求,故可以构造引用public Person(String name, Integer age) {this.name = name;this.age = age;}private String name;private Integer age;
}public class JavaDemo {public static void main(String[] args) {IMessage<Person,String,Integer> msg = Person::new;Person person = msg.create("张三", 20);System.out.println(person);}
}

再来举个构造引用的例子,函数式接口我就不自己创建了,使用 JDK 自带的函数式接口,这些接口都位于 java.util.function 包下。后面会说。
看下 IntFunction< R > 这个接口

package java.util.function;/*** Represents a function that accepts an int-valued argument and produces a* result.  This is the {@code int}-consuming primitive specialization for* {@link Function}.** <p>This is a <a href="package-summary.html">functional interface</a>* whose functional method is {@link #apply(int)}.** @param <R> the type of the result of the function** @see Function* @since 1.8*/
@FunctionalInterface
public interface IntFunction<R> {/*** Applies this function to the given argument.** @param value the function argument* @return the function result*/R apply(int value);
}

这个接口顾名思义,就是一个 int 的功能函数接口,可以将 int 转换成其他类型,也就是 R,这个 R 由我们指定。那么有 int,肯定还有 long 等其它基本类型,有兴趣可以自己看看,效果都一样。
那么我们可以创建一个 User 类,该 user 类里面有一个 age 属性,并且有一个有参的构造参数。

@Data
public class User {private int age;public User() {}/*** User 的有参构造方法就是传入年龄,* 然后返回一个 User 对象* 这和 IFunction<R> 接口功能一样* R apply(int value);** @param age 年龄*/public User(int age) {this.age = age;}}

此时我们就可以来使用构造引用来创建 User 对象。

    @Testpublic void constructReference() {// 构造引用创建 User 对象就是 IntFunction 的一个实现类IntFunction<User> userIntFunction = User::new;//  调用有参构造器创建 User 对象User user = userIntFunction.apply(10);System.out.println(user);}

也可以通过 IntFunction< R > 来创建数组对象,举个例子。

    @Testpublic void constructReference2() {// 构造引用创建 Long[] 对象就是 IntFunction 的一个实现类IntFunction<Long[]> userIntFunction = Long[]::new;// 通过有参构造器来给数组长度赋值 ==> new Long[10];Long[] longArr = userIntFunction.apply(10);System.out.println("数组长度: " +longArr.length);System.out.println(Arrays.toString(longArr));}

JDK8自带函数式接口

在JDK1.8之中,提供有Lambda表达式和方法引用,但是你会发现如果由开发者自己定义函数式的接口,往往都需要使用@FunctionalInterface来进行大量的声明,于是很多的情况下如果为了方便则可以引用系统中提供的函数式接口。

在系统之中专门提供有一个java.util.functional的开发包,里面可以直接使用函数式接口,在这个包下面一共有如下几个核心的接口供我们使用。

功能型函数式接口

接口定义 接口作用 接口使用
@FunctionalInterface
public interface Function<T,R>{ public R apply(T t); }
消费 T 类型参数,返回 R 类型结果 如下所示

function 相当于是给一个参数,然后返回一个结果。
如果是给两个参数,返回一个结果,那么就是 BiFunction。Bi 前缀即使 binary 的缩写。

import java.util.function.*;
/*@FunctionalInterfaceT是参数类型R是返回类型public interface Function<T,R>{public R apply(T t);}*/
class StringCompare {// 给一个 String 类型的参数,返回布尔类型,符合功能性函数式接口的抽象方法public static boolean test(String t) {return t == null;}
}
public class JavaDemo {public static void main(String[] args) {// 直接静态引用Function<String,Boolean> func1 = StringCompare::test;System.out.println(func1.apply(null));}
}

消费型函数式接口

消费性函数式接口,只能进行数据的处理操作,而没有返回值

​ · 在进行系统输出的时候使用的是:System.out.println();这个操作只是进行数据的输出和消费,而不能返回,这就是消费性接口。

接口定义 接口作用 接口使用
@FunctionalInterface
public interface Consumer{ public void accept(T t); }
接收一个 T 类型参数,但是不返回任何东西,消费型接口 如下

其实最常见的消费型接口的实现,就是 System.out.println(xxx) 了。 我们只管往方法中输入参数,但是并没有返回任何值。
Consumer 相当于是有来无回,给一个参数,但是无返回。
而如果是两个参数,无返回,那么就是 BiConsumer。

public class JavaDemo {public static void main(String[] args) {Consumer<String> consumer = System.out::println;consumer.accept("Hello World!");}
}

当然我们也可以自定义消费性接口

class StringCompare {// 接收 StringBuilder ,但是不返回任何数据。public void fun(StringBuilder sb) {sb.append("World!");}
}public class JavaDemo {public static void main(String[] args) {StringBuilder sb = new StringBuilder();sb.append("Hello ");Consumer<StringBuilder> consumer = new StringCompare()::fun;consumer.accept(sb);System.out.println(sb.toString());}
}

供给型函数式接口

接口定义 接口作用 接口使用
@FunctionalInterface
public interface Supplier{public T get();}
啥也不接受,但是却返回 T 类型数据,供给型接口 如下

Supplier 相当于是无中生有,什么也不传,但是返回一个结果。
像 String 类里的 toUpperCase() 方法,也是不接受参数,但是返回 String 类型,就可以看成这个供给型函数式接口的一个实现。

public String toUpperCase() {return toUpperCase(Locale.getDefault());}
import java.util.function.*;
public class Demo01 {public static void main(String[] args) {Supplier <String> sup = "WWW.BAIDU.COM" :: toLowerCase;System.out.println(sup.get());}
}

断言型函数式接口

接口定义 接口作用 接口使用
@FunctionalInterface
public interface Predicate{ public boolean test(T t);}
传入 T 类型参数,返回布尔类型,常常用于对入参进行判断 如下
class StringFilter {// 对集合中的数据进行过滤,传入断言型接口进行判断public static List<String> filter(List<String> list, Predicate<String> predicate) {List<String> stringList = new ArrayList<>();for (String str : list) {if (predicate.test(str)) {stringList.add(str);}}return stringList;}}public class JavaDemo {public static void main(String[] args) {List<String> stringList = Arrays.asList("好诗", "好文", "好评", "好汉", "坏蛋", "蛋清", "清风", "风间");List<String> filterList = StringFilter.filter(stringList, list -> list.contains("好"));System.out.println(filterList);}
}

如果JDK本身提供的函数式接口可以被我们所使用,那么就没必要重新去定义了。

总结

在开发中,使用 lambda 表达式能提高开发效率,写出更加 “高大上” 的代码。但 lambda 可读性较差,如果没接触过过的程序员不友好。

想要掌握 lambda 表达式,还是要多练。从手写一个接口的实现类,到匿名内部类,到 lambda。一开始不能直接写出 lambda ,可以先用匿名内部类的方式,然后一步步简化,多敲多练,最终就能游刃有余。

Lambda从入门到精通(一篇搞懂)相关推荐

  1. python 类-Python入门--一篇搞懂什么是类

    原标题:Python入门--一篇搞懂什么是类 写一篇Python类的入门文章,在高级编程语言中,明白类的概念和懂得如何运用是必不可少的.文章有点长,3000多字. Python是面向对象的高级编程语言 ...

  2. 《Autosar从入门到精通-实战篇》总目录_培训教程持续更新中...

    目录 一.Autosar入门篇: 1.1 DBC专题(共9篇) 1.2 ARXML专题(共35篇) 1.2.1 CAN Matrix Arxml(共28篇) 1.2.2 ASWC Arxml(共7篇) ...

  3. Linux开发从入门到精通——基础篇 :1、计算机常识、Linux操作系统和文件系统介绍

    Linux开发从入门到精通--基础篇 :1.计算机常识.Linux操作系统和文件系统介绍

  4. 一篇搞懂OOA/OOD/OOP的区别

    文章目录 OOA OOD OOP 总结 相关文章: 一篇搞懂OOA/OOD/OOP的区别 面向对象的基本原则-抽象,封装,继承,分解 GRASP模式概述 面向对象的六大原则 OOA什么鬼,OOD又是什 ...

  5. C++ 一篇搞懂多态的实现原理

    C++ 一篇搞懂多态的实现原理 虚函数和多态 01 虚函数 在类的定义中,前面有 virtual 关键字的成员函数称为虚函数: virtual 关键字只用在类定义里的函数声明中,写函数体时不用. cl ...

  6. 一篇搞懂微信小程序以及和其他对比

    一篇搞懂微信小程序以及和其他对比** 前两年的文章了,现在小程序肯定是有变化的,作为自己的随记 一.产品定位及功能分析** 微信小程序是一种全新的连接用户与服务的方式,他可以在微信内被便捷的获取和传播 ...

  7. 一篇搞懂关于计算机的减法运算

    一篇搞懂关于计算机的减法运算 减法 相减结果为正的减法 相减结果为负数的减法 减法 相减结果为正的减法 如下一篇拙言,是自己平时的总结,如有错误欢迎各位大佬指正. 相信你一定听说过,补码,取反加一等等 ...

  8. Flink从入门到精通100篇(二十一)-万字长文详解 Flink 中的 CopyOnWriteStateTable

    前言 现如今想阅读 HashMap 源码实际上比较简单,因为网上一大堆博客去分析 HashMap 和 ConcurrentHashMap.本文详细分析 CopyOnWriteStateTable 源码 ...

  9. 别翻了,Lambda 表达式入门,看这篇就够了

    今天分享的主题是<Lambda 表达式入门>,这也是之前一些读者留言强烈要求我写一写的,不好意思,让你们久等了,现在来满足你们,为时不晚吧? 01.初识 Lambda Lambda 表达式 ...

最新文章

  1. java.lang.NoSuchMethodError: org.springframework.web.context.support.XmlWebApplicationContext.getEnv
  2. 代码工具 | 数据清洗,试试这 8套Python代码
  3. linux系统主要常见目录结构
  4. C#中RichEdit控件,保存文本和图片到mysql数据库
  5. ASCII码与string的相互转换
  6. 1千条数据平均分配给15人_5项数据挂零!但5犯太吓人!CBA第1狠人场均干1人
  7. 【转】QT布局QGridLayout QHBoxLayout QVBoxLayout简要分析!!
  8. dubbo 相关面试题 有用
  9. 数据结构思维 第十三章 二叉搜索树
  10. 工信部:支持符合条件的工业互联网企业上市
  11. PDF怎么翻译成中文?这些方法值得收藏
  12. iframe透明设置
  13. 安川ga700变频器故障码集_安川变频器故障代码和报警参数大全
  14. 惠普m227fdw引擎通信错误_惠普m227fdw/m132nw提示耗材余量错误解决方案
  15. 协同控制中的共识算法概述
  16. 产品读书《关键对话:如何高效能沟通》
  17. [流媒体]实例解析MMS流媒体协议,下载LiveMediaVideo[1][修正版,增加了带宽测试包]
  18. 直观上理解PCA中特征值和特征向量
  19. 条纹噪声图片_红外图像条纹噪声消除方法
  20. CSDN图片去水印干货

热门文章

  1. Android 操作系统简介
  2. esxi 部署模板_烂泥:vcenter通过模板部署vm
  3. 使用Dism++备份系统文件并恢复
  4. 大长今(汤灿版) 歌词
  5. 什么是中华田园敏捷开发?
  6. 冷静分析:Opteron优势和潜在问题 (也是完全从网上copy的)
  7. Hive正则表达式对数据过滤
  8. [USACO13NOV]挤奶牛Crowded Cows(洛谷 P3088)
  9. 在网页版 BOSS直聘 上实现 批量打招呼,发信息等操作
  10. MATLAB基础编程(005-01)之Resize an Image with imresize Function 使用imresize函数 调整图像大小