Java8新特性:Lambda表达式

目录

java8新特性:Lambda表达式

一 Java8新特性简介

二 案例切入:

2.1 案例需求1:获取年龄小于35的员工信息

2.2 案例需求2:获取工资大于5000的员工信息

2.3 优化方式1:策略设计模式

2.4 优化方式2:匿名内部类

2.5 优化方式3:Lambda表达式

2.6 优化方式4:Stream API

三 Lambda表达式语法

3.1 语法:

3.2 语法练习

四 Lambda表达式应用

4.1 应用1:对一个数进行运算

4.2 Lambda表达式总结

五 Java8新特性:函数式接口导论


 

一 Java8新特性简介

Java8主要加入了如下几个新特性:

1. Lambda 表达式
2. 函数式接口
3. 方法引用与构造器引用
4. Stream API
5. 接口中的默认方法与静态方法
6. 新时间日期 API
7. 其他新特性

其中最重要的莫过于Lambda表达式和Stream API。本篇博客主要介绍Lambda表达式。

二 案例切入:

员工类pojo:这是Employee类的代码,省略了构造器,getter和setter方法以及重写的hashcode(),equals(),toString()方法等代码

public class Employee {

	private int id;
	private String name;
	private int age;
	private double salary;
	//空参,全参构造器
	//getter,setter方法
	//重写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)
	);

2.1 案例需求1:获取年龄小于35的员工信息

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;
}

这是过滤年龄的方法,传入一个元素都是员工的集合,会得到筛选条件为年龄小于等于35的员工集合。

我们可以对filterEmployeeAge方法进行测试。

	@Test
	public void test3(){
		List<Employee> list = filterEmployeeAge(emps);
		
		for (Employee employee : list) {
			System.out.println(employee);
		}
	}

输出结果如下:

2.2 案例需求2:获取工资大于5000的员工信息

可以仿照filterEmployeeAge方法再写一个按照工资过滤的方法filterEmployeeSalary

	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;
	}

对比filterEmployeeAge和filterEmployeeSalary方法,可以发现这两个过滤方法的代码主要区别就是if()里面的判断条件,符合相应条件的加入集合。如果还有其它的需求:例如姓名长度,性别,部门,工资小于3500,年龄大于60等过滤条件,我们还得再写相应的方法;而这些方法的区别仍是if()里面的判断条件,我们却得写很多重复的代码,增大很多不必要的工作量。

说到这里,很多人就会想到了策略模式就是解决这个问题的。(接下来使用策略模式优化上面的代码,顺便复习一下策略设计模式)

2.3 优化方式1:策略设计模式

将if()里面的判断条件抽取出来作为一个接口。

public interface MyPredicate<T> {

	public boolean test(T t);
	
}

然后,不同的过滤条件封装在MyPredicate接口的实现类中

按年龄过滤条件实现类:

public class FilterEmployeeForAge implements MyPredicate<Employee>{

	@Override
	public boolean test(Employee t) {
		return t.getAge() <= 35;
	}

}

按工资过滤条件实现类:

public class FilterEmployeeForSalary implements MyPredicate<Employee> {

	@Override
	public boolean test(Employee t) {
		return t.getSalary() >= 5000;
	}

}

针对接口写统一的过滤员工方法,不同的过滤条件只需要传入相应的MyPredicate接口实现类即可以。

public List<Employee> filterEmployee(List<Employee> emps, MyPredicate<Employee> mp){
		List<Employee> list = new ArrayList<>();
		
		for (Employee employee : emps) {
			if(mp.test(employee)){
				list.add(employee);
			}
		}
		
		return list;
	}

测试: 


	@Test
	public void test4(){
		List<Employee> list = filterEmployee(emps, new FilterEmployeeForAge());
		for (Employee employee : list) {
			System.out.println(employee);
		}
		
		System.out.println("------------------------------------------");
		
		List<Employee> list2 = filterEmployee(emps, new FilterEmployeeForSalary());
		for (Employee employee : list2) {
			System.out.println(employee);
		}
	}

2.4 优化方式2:匿名内部类

事实上,我们在使用策略模式进行优化的时候,并不会写MyPredicate接口的实现类,而是将MyPredicate接口的匿名内部类作为参数传入filterEmployee方法。

//优化方式二:匿名内部类
	@Test
	public void test5(){
		List<Employee> list = filterEmployee(emps, new MyPredicate<Employee>() {
			@Override
			public boolean test(Employee t) {
				return t.getId() <= 3;
			}
		});
		
		for (Employee employee : list) {
			System.out.println(employee);
		}
	}

2.5 优化方式3:Lambda表达式

说到匿名内部类作为参数传递给方法,就离Lambda表达式非常接近了。Lambda表达式就是对匿名内部类作为参数传递给方法进行优化。

这里先直接给出Lambda表达式优化后的结果,大家可以感受下代码是多么的简洁。

	//优化方式三:Lambda 表达式
	@Test
	public void test6(){
		List<Employee> list = filterEmployee(emps, (e) -> e.getAge() <= 35);
		list.forEach(System.out::println);
		
		System.out.println("------------------------------------------");
		
		List<Employee> list2 = filterEmployee(emps, (e) -> e.getSalary() >= 5000);
		list2.forEach(System.out::println);
	}

2.6 优化方式4:Stream API

这里也是直接给出结果,后续解释。

	//优化方式四:Stream API
	@Test
	public void test7(){
		//年龄小于等于35的员工信息
		emps.stream()
			.filter((e) -> e.getAge() <= 35)
			.forEach(System.out::println);
		
	}

三 Lambda表达式语法

Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。

箭头操作符的左侧传递给接口内抽象方法的形参,箭头操作符右侧作为接口内抽象方法的方法体。

3.1 语法:

Lambda 表达式的基础语法:Java8中引入了一个新的操作符 "->" 该操作符称为箭头操作符或 Lambda 操作符
箭头操作符将 Lambda 表达式拆分成两部分:
左侧:Lambda 表达式的参数列表
右侧:Lambda 表达式中所需执行的功能, 即 Lambda 体

语法格式一:无参数,无返回值
    () -> System.out.println("Hello Lambda!");
 
语法格式二:有一个参数,无返回值
     (x) -> System.out.println(x)
 
语法格式三:若只有一个参数,小括号可以省略不写(为了方便阅读和理解,常常不会省略小括号,即采用语法格式二那样)
     x -> System.out.println(x)
 
语法格式四:有两个以上的参数,有返回值,并且 Lambda 体中有多条语句(有多条语句就需要大括号,并将多条语句放入其中)
     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编译器通过上下文推断出,数据类型,即“类型推断”
     (Integer x, Integer y) -> Integer.compare(x, y);
 
 上联:左右遇一括号省
 下联:左侧推断类型省
 横批:能省则省

3.2 语法练习

	//语法格式一:无参,无返回值
	@Test
	public void test1(){
		int num = 0;//jdk 1.7 前,必须是 final
		//创建线程(未使用Lambda表达式)
		Runnable r = new Runnable() {
			@Override
			public void run() {
				System.out.println("Hello World!" + num);
			}
		};
		
		r.run();
		
		System.out.println("-------------------------------");

		//使用Lambda表达式
		Runnable r1 = () -> System.out.println("Hello Lambda!");
		r1.run();
	}

	//语法格式三:一参,无返回值
	@Test
	public void test2(){
		Consumer<String> con = x -> System.out.println(x);
		con.accept("我爱Lambda!");
	}

	//语法格式四:两参,有返回值,多条语句
	@Test
	public void test3(){
		Comparator<Integer> com = (x, y) -> {
			System.out.println("函数式接口");
			return Integer.compare(x, y);
		};
	}
	//语法格式六:类型推断
	@Test
	public void test4(){
		Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
	}

四 Lambda表达式应用

上面仅仅是对Lambda表达式的几种语法进行介绍,接下来将介绍Lambda表达式如何在实际编程中使用。还记得之前案例切入处的一句话吗:Lambda表达式就是对匿名内部类作为参数传递给方法进行优化。

Lambda表达式的作用就是取代某接口的匿名内部类作为参数传递给方法。

4.1 应用1:对一个数进行运算

运算方法:operation

//案例1:对一个数进行运算
public Integer operation(Integer num, MyFun mf){
	return mf.getValue(num);
}

具体怎么运算运用策略模式的思想封装在MyFun接口中,由MyFun接口的匿名内部类进行具体实现。

public interface MyFun {

	public Integer getValue(Integer num);
	
}

以下是使用匿名内部类和Lambda表达式的对比。

	//应用1:对一个数进行运算
	@Test
	public void test6(){
		//匿名内部类
		//对该数取平方
		Integer num1=operation(100, new MyFun() {
			@Override
			public Integer getValue(Integer num) {
				return num*num;
			}
		});
		System.out.println(num1);
		//Lambda表达式
		//对该数取平方(一参,有返回值,省略return)
		Integer num2 = operation(100, (x) -> x * x);
		System.out.println(num2);
		//该数加200
		System.out.println(operation(200, (y) -> y + 200));
	}

4.2 Lambda表达式总结

Lambda表达式就是取代接口的匿名内部实现类作为参数传递给一个方法,箭头操作符->左侧内容等价于匿名内部类实现接口重写其方法时需要传入的参数;箭头操作符右侧内容等价于匿名内部类实现接口重写其方法时的方法体。

五 Java8新特性:函数式接口导论

通过上面的总结,我们会发i西安一个问题:如果Lambda表达式所取代的匿名内部类实现的接口含有多个抽象方法时,Lambda表达式箭头操作符的左侧参数是传递给那个抽象方法作为形参的?箭头操作符右侧又是哪个抽象方法的方法体?

这就引出了函数式接口,也就是要求Lambda表达式传递的接口内只能有一个抽象方法,此即为函数式接口。

函数式接口的内容将在这篇博客中介绍:Java8新特性:函数式接口

 

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐