函数式接口
函数式接口是1.8中的新特性,他不属于新语法,更像是一种规范
面向对象接口复习
在使用lambda表达是之前先回顾一下面向对象的接口,创建接口的关键字为interface
,这里创建一个日志接口:
public interface LogService {
void info();
}
众所周知,Java中的接口是不能直接创建实例的,因为他的抽象方法没有得到实现:
当我们想要创建接口实例的时候一般都通过实现类来获取实例:
public class LogServiceImpl implements LogService {
@Override
public void info() {
System.out.println("哈哈哈哈");
}
}
这样我们就可以通过实现来创建这个接口的实例:
public class ApplicationMain {
public static void main(String[] args) {
LogService log = new LogServiceImpl();
log.info();
}
}
对接口中的方法进行实现除了实现类之外,还可以通过匿名类的方式来创建接口的实例:
public class ApplicationMain {
public static void main(String[] args) {
LogService log = new LogService() {
@Override
public void info() {
System.out.println("嘿嘿嘿");
}
};
}
}
什么是函数式接口
当一个接口只有一个抽象方法 ( 接口默认实现不算 ) 的时候这个接口就是函数式接口,上面的LogService
就属于函数式接口,还有学习线程时的Runnable
接口也属于函数式接口
函数式接口还有一个注解为@FunctionalInterface
,据我了解这个注解主要起到的是检查的作用,当接口中的抽象方法数量大于1的时候就会报错:
内置常用函数式接口
Java中内置了一些常用的函数式接口,这里介绍其中的四种,不用刻意去记,用到就明白了
函数式接口 | 参数 | 返回值 | 说明 |
---|---|---|---|
Consumer |
T | void | 对类型为T的实例进行操作 |
Supplier |
无 | T | 返回类型为T的实例 |
Function<T, R> | T | R | 通过对T的操作返回R的实例 |
Predicate |
T | boolean | 通过对T的操作返回boolean值 |
通过巧妙的运用函数式接口可以实现回调函数的功能
Lambda表达式
Lambda表达式是1.8中的新特性,主要针对函数式接口的匿名实现做了简化
表达式基本语法
找到上面匿名内部类创建LogService
的代码:
public class ApplicationMain {
public static void main(String[] args) {
LogService log = new LogService() {
@Override
public void info() {
System.out.println("嘿嘿嘿");
}
};
}
}
因为创建的实例就是接口本身,所以new
的部分就可以省略不写,接口中需要实现的抽象方法只有一个,那么也没有必要指定具体实现的方法名称,简写后如下所示:
public class ApplicationMain {
public static void main(String[] args) {
LogService log = ()->{
System.out.println("哈哈哈哈");
};
log.info();
}
}
Lambda表达式前面的括号( )
就代表参数列表,箭头->
后面的代码块就代表着实现的具体方法体,如果info
方法中有参数就可以这样写:
public class ApplicationMain {
public static void main(String[] args) {
LogService log = (msg)->{
System.out.println(msg);
};
log.info("老八秘制小汉堡");
}
}
使用Lambda表达式创建一个线程:
public class ApplicationMain {
public static void main(String[] args) {
// Runnable为函数式接口,所以可以使用Lambda表达式
new Thread(()->{
System.out.println("另一条线程");
}).start();
System.out.println("Main主线程");
}
}
表达式的简写
使用Lambda表达式代替匿名内部类的写法非常简洁,但是在满足特定条件的情况下,它还可以更简洁:
public class ApplicationMain {
public static void main(String[] args) {
/**
* 在参数列表只有一个参数的情况下,()可以省略不写
* 当代码块中只有一句代码时{}可以省略不写
* 当函数有返回值且代码块中只有一句代码时return可以省略不写
*/
LogService log = msg->System.out.println(msg);
log.info("老八秘制小汉堡");
}
}
Lambda表达式相比匿名内部类还有一个特点,当使用匿名内部类时会为匿名内部类生成一个class文件,而使用Lambda表达式时就不会额外生成class文件,这个不做重点
方法引用
方法引用是1.8中的新特性,配合Lambda表达式使用可以让代码更简洁
方法引用初体验
方法引用中需要的特殊符号为双冒号::
,具体表现为以下形式:
表现形式 | 作用 |
---|---|
对象::实例方法 | 通过对象调用实例中的方法 |
类::实例方法 | 通过类调用实例中的方法 |
类::静态方法 | 通过类调用实例中的静态方法 |
类::new | 创建该类的实例 |
当Lambda表达式中只有一句调用代码,且调用的目标函数的参数列表和Lambda表达式接收的参数列表一致,那么就可以省略参数列表简写为以下这种形式:
public class ApplicationMain {
public static void main(String[] args) {
// 代表的就是上面表格中的第一行,对象::实例方法
LogService log = System.out::println;
log.info("老八秘制小汉堡");
}
}
综合练习
针对上面的函数式接口,Lambda表达式以及方法引用做练习,代码会有些多:
/**
* 综合练习
* 函数式接口 + Lambda表达式 + 方法引用
*/
public class ApplicationMain {
public static void main(String[] args) {
// 方法引用 对象::实例方法
strToDate("2021-2-20 22:08:45", System.out::println);
// 方法引用 类::new
System.out.println(dateToStr(Date::new));
// 回调函数:取最大值
Integer integer = strListToIntList(Arrays.asList("135", "541", "244"), list -> {
int maxNumber = 0;
for (Integer item : list) {
if (item > maxNumber) {
maxNumber = item;
}
}
return maxNumber;
});
System.out.println("最大值为:" + integer);
// 实现过滤器功能
List<Integer> list = filter(Arrays.asList(800, 200, 1200, 600, 3000, 2400), item -> item > 1000);
System.out.println(list);
}
// Consumer:通过时间字符串快速获取Date对象
public static void strToDate(String date, Consumer<Date> consumer) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date parse = null;
try {
parse = sdf.parse(date);
} catch (ParseException e) {
System.err.println("转换失败!");
}
consumer.accept(parse);
}
// Supplier:通过Date对象快速获取到时间字符串
public static String dateToStr(Supplier<Date> supplier) {
Date date = supplier.get();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(date);
}
// Function:将String集合转换为Integer,后续操作交给调用者
public static Integer strListToIntList(List<String> list, Function<List<Integer>, Integer> function) {
ArrayList<Integer> integerList = new ArrayList<>();
for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
String next = iterator.next();
Integer integer = Integer.valueOf(next);
integerList.add(integer);
}
return function.apply(integerList);
}
// predicate:返回大于1000的数字
public static List<Integer> filter(List<Integer> list, Predicate<Integer> predicate) {
ArrayList<Integer> target = new ArrayList<>();
for (Integer item : list) {
if (predicate.test(item)) {
target.add(item);
}
}
return target;
}
}