JAVA8新特性-lambda表达式

|▌冷眼眸甩不掉的悲伤 提交于 2019-12-01 16:09:01

摘要

一篇让你搞懂lambda表达式、函数式接口、方法引用、Optional这些JAVA8的新特性及相互间关系。下图是本文的内容概要
在这里插入图片描述

函数式接口(Functional Interface)

如果一个接口的定义带有@FunctionanlInterface注解,它就是函数式接口。但其实函数式接口不一定非要带这个注解,只要满足一个条件:只有一个抽象的方法。

注意:静态方法,默认方法和覆盖了Object的方法都不算抽象方法

我们来定义一个函数式接口:

@FunctionalInterface
public interface Counter {
	@Override
	boolean equals(Counter c);
    
    public static String toString(Counter c);
    
    public int count();
}

这是计数器接口只有一个计数方法count()是抽象方法

JDK 内置的函数函数式接口方法

1,Consumer<T>
T为输入参数类型,执行某个动作但无返回值,例如

Consumer<String> print = (x)-> {
	System.our.println(x);
}

等号前是函数式接口print的定义,等号后就是lambda表达式,这里可以先透漏下lambda表达式的类型就是函数式接口,后面会说明.

2,Supplier<T>
无输入,返回类型是T

3,Predicate<T>
T为输入类型,返回boolean

4,Function<T,R>
T为输入类型,R为返回值类型

5, BinaryOperator<T>
输入参数有两个,类型都是T,返回值类型也是T,常见用于reduce操作

JDK内置函数式接口的其他常见的还有Runable, Callable,Comparator

lambda表达式(Lambda Express)

() => System.out.println("This is a lambda express");

上面就是个简单的lambda表达式,上文中看到它是可以被赋值给一个变量的。这个变量的类型是函数式接口,而Lambda表达式本身就是函数式接口中定义的那个唯一的抽象方法的实现。

lambda表达式最直观的作用就是使代码非常简洁,有两个场景非常适合使用lambda表达式

1, 函数式接口的实现只需要用到一次

public void testMethod(Printer<String> printer, String content){
	printer.print(content);
}

假设testMethod方法作用是使用接口printer打印第二个参数中字符串.

如果是JAVA7首先必须定义好这个接口的实现类,然后在用到接口方法的时候,需要先创建实现类然后才能调用方法。

// java7
// 必须先定义好实现类
public class PrinterImpl implements Printer {
	@Override
	public void print(String s){
		System.our.println(s);
	}
}
// 先创建实现类实例,再完成调用
Printer printer = new PrinterImpl();
testMethod(printer, "hello");

在JAVA8 中使用lambda表达式就简洁多了

// java8
testMethod((x)->System.out.println(x), "hello");

而且最重要的是:如果接口实现只需要用一次,你的项目中可以省去一个类。

2, 与forEach, Stream API及方法引用结合使用

假设有如下学生类定义

public class Student {
	private String firstName;
    private String lastName;
    private int age;
    // 省略getter和setter方法
}

现有几个学生,需要找出firstName以字母M开头的并打印

List<Student> superStudents = Arrays.asList(
	new Student("Huateng","Ma", 4);
	new Student("Yun","Ma", 5);
	new Student("SK", "Ma", 6);
	new Student("Qiangdong", "Liu", 4)
); 
//notice
superStudents.stream().filter(s->s.getFirstName.startWith("M")).forEach(System.out::print); 

看//notice下面那句是否非常简洁优雅。你可能现在不明白stream是什么,还有forEach里的表达式是怎么回事。没关系,往下看就知道了。

方法引用(Method References)

上个实例代码中, System.out::print就是方法应用的写法,它可以进一步简化lambda表达式.
可以看到,你只需要使用方法的名字就行,具体的调用会交给函数式接口处理。除了普通方法,构造器方法和静态方法也可以使用方法引用。

1, 构造器方法引用

格式: Class::new,调用默认构造器

2, 静态方法引用

格式: Class::static_method

3,普通方法引用

格式1:Class::method,方法不能带参数
格式2:instance::method

Optional

最后介绍下空指针异常的大杀器Optional.它单独使用时简化代码效果不明显,需要和lambda表达式配合使用。比如下面几种常见写法:

1, 若存在,执行动作

// 假设stuOpt是Optional包装的Student对象
stuOpt.ifPresent(doSomething())

2,若存在,则返回被包装对象,否则返回空

return stuOpt.orElse(null)

3, 若存在,则返回被包装对象,否则执行方法获取

return stuOpt.orElseGet(()->fetchNew())

4, 嵌套null检查

return stuOpt.map(s->s.getSchool()).map(school->school.getPresent()).orElse(null);

总结

lambda表达式可以使代码异常简洁,结合方法引用可以进一步简化代码。lambda表达式可被赋值给函数式接口,它本身就是函数式接口中抽象方法的实现。lambda表达式结合Optional使用可以简化对null的处理。

至于Steam API的介绍,参考Stream API概念及用法详解

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!