当前位置: 首页 > news >正文

JDK8新特性之Lambda表达式快速入门

目录标题

  • 为什么使用 Lambda 表达式
    • 示例一:先看一个常用排序类Comparator的示例
    • 示例二:筛选员工数据的示例
      • 传统方式实现的示例
      • 策略模式优化的示例
  • Lambda 基础语法
    • 语法格式一:无参数,无返回值
    • 语法格式二:有一个参数,并且无返回值
    • 语法格式三:若只有一个参数,小括号可以省略不写
    • 语法格式四:有两个以上的参数,有返回值,并且 Lambda 体中有多条语句
    • 语法格式五:若 Lambda 体中只有一条语句, return 和 大括号都可以省略不写
    • 语法格式六:Lambda 表达式的参数列表的数据类型可以省略不写
  • Lambda 表达式需要“函数式接口”的支持
    • JDK8自带的函数式接口

为什么使用 Lambda 表达式

示例一:先看一个常用排序类Comparator的示例

Comparator<String> com = new Comparator<String>(){@Overridepublic int compare(String o1, String o2) {return Integer.compare(o1.length(), o2.length());}
};
TreeSet<String> ts = new TreeSet<>(com);

代码再简化一点,匿名内部类写法:

TreeSet<String> ts = new TreeSet<>(new Comparator<String>(){@Overridepublic int compare(String o1, String o2) {return Integer.compare(o1.length(), o2.length());}
});

简化后还是有代码冗余,实际有用的代码就“Integer.compare(o1.length(), o2.length())” 这一行。
这时Lambda 表达式闪亮登场:

Comparator<String> com = (o1, o2) -> Integer.compare(o1.length(), o2.length());
TreeSet<String> ts = new TreeSet<>(com);

代码是不是简洁很多了。

示例二:筛选员工数据的示例

员工类:

public class Employee {private int id;private String name;private int age;private double salary;// get()、set()、hashCode()、equals()、toString()省略
}

现在有一批员工数据:

List<Employee> emps = Arrays.asList(new Employee(101, "张三", 18, 9999.99),new Employee(102, "李四", 59, 6666.66),new Employee(103, "王五", 28, 3333.33),new Employee(104, "赵六", 8, 7777.77),new Employee(105, "田七", 38, 5555.55)
);

传统方式实现的示例

有以下业务需求,通过传统方式实现如下:

/*** 需求1:获取公司中年龄小于 35 岁的员工信息* @param emps* @return*/
public List<Employee> filterEmployeeAge(List<Employee> emps){List<Employee> list = new ArrayList<>();for (Employee emp : emps) {if(emp.getAge() <= 35){list.add(emp);}}return list;
}/*** 需求2:获取公司中工资大于 5000 的员工信息* @param emps* @return*/
public List<Employee> filterEmployeeSalary(List<Employee> emps){List<Employee> list = new ArrayList<>();for (Employee emp : emps) {if(emp.getSalary() >= 5000){list.add(emp);}}return list;
}

仔细观察几个方法发现只有if()判断一行代码不一样,其他代码都相同。

优化思路:提取封装变化的部分,相同代码复用,另外还要考虑再有类似需求的扩展性,比如按性别过滤、按姓氏过滤等。此时我们会想到一种设计模式:策略模式。

策略模式优化的示例

定义顶层接口:

@FunctionalInterface
public interface MyPredicate<T> {boolean test(T t);
}

根据业务需求,定义接口的实现类,也即上面变化的部分:

/*** 按年龄过滤的类*/
public class FilterEmployeeForAge implements MyPredicate<Employee>{@Overridepublic boolean test(Employee t) {return t.getAge() <= 35;}
}/*** 按工资过滤的类*/
public class FilterEmployeeForSalary implements MyPredicate<Employee> {@Overridepublic boolean test(Employee t) {return t.getSalary() >= 5000;}
}

相同的部分,也即不变的部分:

public List<Employee> filterEmployee(List<Employee> emps, MyPredicate<Employee> predicate){List<Employee> list = new ArrayList<>();for (Employee employee : emps) {if(predicate.test(employee)){ //这一行具体实现类实现list.add(employee);}}return list;
}

策略模式实现上面业务需求:

//需求1:获取公司中年龄小于 35 的员工信息
List<Employee> list = filterEmployee(emps, new FilterEmployeeForAge());
for (Employee employee : list) {System.out.println(employee);
}//需求2:获取公司中工资大于 5000 的员工信息
List<Employee> list2 = filterEmployee(emps, new FilterEmployeeForSalary());
for (Employee employee : list2) {System.out.println(employee);
}

这样,业务代码过滤员工数据时就简洁了很多,也满足了代码的开闭原则。
如果以后有其他过滤员工信息的需求,比如按性别过滤的需求,我们再新建一个过滤实现类即可。
但如果过滤的业务需求很多,那么要新建的过滤实现类也要很多。可以不新建那么多小类吗?
可以,方式一:匿名内部类

List<Employee> list = filterEmployee(emps, new MyPredicate<Employee>() {@Overridepublic boolean test(Employee t) {return t.getSalary() >= 5000;}
});
for (Employee employee : list) {System.out.println(employee);
}

方式二:Lambda表达式

List<Employee> list = filterEmployee(emps, (e) -> e.getSalary() >= 5000);
list.forEach(System.out::println);

对比一下,Lambda表达式的方式是不是代码更简洁清晰。


Lambda 基础语法

Java8中引入了一个新的操作符 “->”, 该操作符称为箭头操作符或 Lambda 操作符。

箭头操作符将 Lambda 表达式拆分成两部分:

  • 左侧:Lambda 表达式的参数列表
  • 右侧:Lambda 表达式中所需执行的功能, 即实现类的方法体

语法格式一:无参数,无返回值

() -> System.out.println(“Hello Lambda!”);

int num = 0;//jdk1.7及以前必须加final
Runnable r = new Runnable() {@Overridepublic void run() {System.out.println("Hello World!" + num);}
};
r.run();Runnable r1 = () -> System.out.println("Hello Lambda!" + num);
r1.run();

语法格式二:有一个参数,并且无返回值

Consumer<String> con = (x) -> System.out.println(x);
con.accept("Hello Lambda!");

语法格式三:若只有一个参数,小括号可以省略不写

Consumer<String> con = x -> System.out.println(x);
con.accept("Hello Lambda!");

语法格式四:有两个以上的参数,有返回值,并且 Lambda 体中有多条语句

有多条语句时,方法体加**{},有返回值加return**。

Comparator<Integer> com = (x, y) -> {System.out.println("函数式接口");return Integer.compare(x, y);
};

语法格式五:若 Lambda 体中只有一条语句, return 和 大括号都可以省略不写

Comparator<Integer> com = (x, y) -> Integer.compare(x, y);

语法格式六:Lambda 表达式的参数列表的数据类型可以省略不写

因为JVM编译器通过上下文推断出,数据类型,即“类型推断”

Comparator<Integer> com = (Integer x, Integer y) -> Integer.compare(x, y);
//根据前面Comparator<Integer>里指定的Integer推断的
Comparator<Integer> com = (x, y) -> Integer.compare(x, y);

String[] arr = {“aaa”, “bbb”, “ccc”}; //值简写,也是根据前面String类型推断的
List< String> list = new ArrayList<>(); //ArrayList<>里没写String,也是根据List< String>里推断出来的


Lambda 表达式需要“函数式接口”的支持

函数式接口:接口中只有一个抽象方法的接口,称为函数式接口。
可以使用注 @FunctionalInterface注解修饰,该注解可以检查是否是函数式接口,比如接口类里定义了2个接口方法,就会提示错误。

JDK8自带的函数式接口

函数式接口参数类型返回类型用途
Consumer< T>Tvoid对类型为T的对象应用操作,包含方法:void accept(T t)
Supplier< T> 供给型接口T返回类型为T的对象,包含方法:T get();
Function< T, R> 函数型接口TR对类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法:R apply(T t);
Predicate< T> 断定型接口Tboolean确定类型为T的对象是否满足某约束,并返回boolean 值。包含方法boolean test(T t);

示例:

@Test
public void test7(){Integer num = operation2(100, (x) -> x * x);System.out.println(num);System.out.println(operation2(200, (y) -> y + 200));
}public Integer operation2(Integer num, Function<Integer, Integer> fun){return fun.apply(num);
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 2024 Java 高分面试宝典 一站式搞定技术面
  • yum install git
  • golang JSON序列化
  • 【Unity2D 2022:UI】TextMeshPro组件无法显示中文
  • Spring Boot中的 6 种API请求参数读取方式
  • 核心线程创建之后是否受keepAliveTime影响?
  • SRv6 和IGP/BGP协议区别
  • 自制熊猫烧香进阶
  • centos安装python 3.9
  • 【diffusers极速入门(五)】扩散模型中的 Scheduler(noise_scheduler)的作用是什么?
  • react函数学习——useState函数
  • 51单片机嵌入式开发:22、STC89C52R控制 实现单总线温度传感器DS18b20的温度读取
  • UltraEdit v27文本代码程序编辑器免费版下载安装教程(亲测可用)
  • Java每日一练,技术成长不间断
  • 第十章 计算机网络——应用层
  • [ 一起学React系列 -- 8 ] React中的文件上传
  • 【140天】尚学堂高淇Java300集视频精华笔记(86-87)
  • cookie和session
  •  D - 粉碎叛乱F - 其他起义
  • MyEclipse 8.0 GA 搭建 Struts2 + Spring2 + Hibernate3 (测试)
  • Redis字符串类型内部编码剖析
  • Three.js 再探 - 写一个跳一跳极简版游戏
  • Traffic-Sign Detection and Classification in the Wild 论文笔记
  • 观察者模式实现非直接耦合
  • 如何利用MongoDB打造TOP榜小程序
  • 思考 CSS 架构
  • 在electron中实现跨域请求,无需更改服务器端设置
  • 1.Ext JS 建立web开发工程
  • 直播平台建设千万不要忘记流媒体服务器的存在 ...
  • #{} 和 ${}区别
  • #NOIP 2014# day.1 生活大爆炸版 石头剪刀布
  • (06)Hive——正则表达式
  • (6)STL算法之转换
  • (BAT向)Java岗常问高频面试汇总:MyBatis 微服务 Spring 分布式 MySQL等(1)
  • (C#)获取字符编码的类
  • (floyd+补集) poj 3275
  • (Matalb分类预测)GA-BP遗传算法优化BP神经网络的多维分类预测
  • (二开)Flink 修改源码拓展 SQL 语法
  • (教学思路 C#之类三)方法参数类型(ref、out、parmas)
  • (每日持续更新)jdk api之FileReader基础、应用、实战
  • (每日一问)基础知识:堆与栈的区别
  • (十)DDRC架构组成、效率Efficiency及功能实现
  • (四)Linux Shell编程——输入输出重定向
  • (转)清华学霸演讲稿:永远不要说你已经尽力了
  • .Net Attribute详解(上)-Attribute本质以及一个简单示例
  • .net的socket示例
  • .NET开源项目介绍及资源推荐:数据持久层
  • .pub是什么文件_Rust 模块和文件 - 「译」
  • /deep/和 >>>以及 ::v-deep 三者的区别
  • @AutoConfigurationPackage的使用
  • @javax.ws.rs Webservice注解
  • @JsonFormat与@DateTimeFormat注解的使用
  • @property括号内属性讲解
  • @SuppressWarnings注解
  • [ Socket学习 ] 第一章:网络基础知识