网站首页 > 基础教程 正文
java8以后增加了lambda表达式,使得java编程也可以像其他语言一样有更简洁的编码风格,而且基于lambda更方便实现链式调用。
一个标准的函数式接口定义如下:
@FunctionalInterface
public interface CharacterProcessor {
String doProcess(String source);
}
其中@FunctionalInterface 不是必须的,它只是一个标记,配合编译器做检查的,你完全可以不用加这一个注解不会影响lambda表达式的正常使用。lambda是针对接口的,而且是只有一个抽象方法的接口(这里的接口是指interface以及抽象class)。如果一个接口中有两个抽象方法,是不能使用lambda表达式的,比如你像下面这样定义:
@FunctionalInterface
public interface CharacterProcessor {
String doProcess(String source);
String process(Member m);
}
如上面这样的接口定义了两个方法在编辑工具中是会报错的,因为有@FunctionalInterface做类型检查,如果你去掉这个注解虽然接口本身不报错了,但是在你引用这个接口使用lambda表达式的时候会报错。所以lambda就是只适用于单一抽象方法的接口。至于为什么只能是一个抽象方法其实很好理解,因为lambda表达式的原理就是基于推导,如果有多个抽象方法编辑器无法推导是哪一种。
定义了接口以后,在JAVA中接口是不能实例化的,必须通过它的实现类才能使用。要实现一个接口,在JAVA中有很多种方式。
第一种:直接new一个内部类,这种方式比较常见
CharacterProcessor a = new CharacterProcessor() {
@Override
public String doProcess(String source) {
return source.concat("abc");
}
};
第二种:新建一个类实现这个接口,然后创建这个实现类的实例,这是最常见的方式
public class CharacterProcessorImpl implements CharacterProcessor {
@Override
public String doProcess(String source) {
return source.toUpperCase();
}
}
CharacterProcessor b = new CharacterProcessorImpl();
第三种 :标准 lambda表达式,任何的函数式接口都可以使用这种方式
CharacterProcessor c = (String v) -> {return v.toUpperCase();};
这是最常规最基本的lambda表达式书写方式,() 里面是参数列表,多个参数用逗号分隔,接着就是一个 -> 箭头符号,再后面就是{} 里面就是具体的实现主体,可以有多行代码,每行代码需要用一个分号结束,也就是平时我们正常的代码块。
第四种:简化版 lambda表达式,接口只有一个入参的时候,括号里的参数类型可以省略
CharacterProcessor d = (v) -> {return v.toUpperCase();};
第五种:更简化版 lambda表达式,只有一个参数时候,连括号都可以省略了
CharacterProcessor e = v -> {return v.toUpperCase();};
第六种;更更简化版 lambda表达式,只有一行的代码主体可以直接省略{}花括号,有返回值也不需要 return关键词
CharacterProcessor f = (String v) -> v.toUpperCase();
第七种:极简化版 lambda表达式 更简洁写法,参数类型、花括号、return 都直接省略了
CharacterProcessor g = v -> v.toUpperCase();
第八种:终极简化版lambda表达式,连参数和箭头都直接不用写了,简直颠覆了我的认知
CharacterProcessor h = String::toUpperCase;
其他几种lambda使用方式也许还好理解,这第八种是咋回事?为什么可以这样写呢?这个 :: 是双冒号语法,作用是方法引用。大致意思是,使用lambda表达式会创建匿名方法, 但有时候需要使用一个lambda表达式只调用一个已经存在的方法, 所以这才有了方法引用。
这种lambda的写法必须符合两个条件:
- 代码主体必须是只有一行代码
- 代码主体里面只是一个方法调用
比如下面的表达式已经够简洁了,但是按照 :: 语法定义还可以更简洁
CharacterProcessor g = v -> v.toUpperCase();
v入参是String类型,方法体也只是简单的调用了String对象的 toUpperCase()方法,并且只有一个方法一行代码,所以符合::语法特征,那就是可以直接使用 String::toUpperCase 这种更简化的写法。
因为lambda简洁并且有一定的模式,所以JDK种内置了四种比较通用的函数式接口
- 消费型 Consumer, 特点是 只有一个入参,没有返回值,使用场景多是用来对某个对象进行自身调用
List<String> list=new ArrayList<>();
list.forEach(v->{
});
如上面代码就是我们经常用到的forEach方法,内部就是基于消费型函数接口,源码如下
- 供给型 Supplier 特点是没有入参,但是有返回值,多用于在返回随机值,因为没有参数所以和外界没有联系,在方法内部自行处理逻辑再返回结果
- 断言型 Predicate 特点是有一个入参,返回一个布尔值,多用于对入参进行逻辑处理返回一个布尔结果的场景
List<String> list=new ArrayList<>();
list.stream().filter(v->v.length()==2).collect(Collectors.toList());
如上所示,java stream种的 filter就是使用断言型函数接口,源码如下:
- 函数型 Function 特点是有一个入参和一个返回值,多用于对入参进行处理并返回处理结果的场景
list.stream().map(String::toUpperCase).collect(Collectors.toList())
如上所示,java stream种的 map就是使用函数型接口做数据处理的,其源码如下图:
以上是JDK源码种的四种内置函数式接口,实际中我们可以扩展出很多的函数接口,比如两个入参的,只要符合函数式接口定义就可以了。
lambda表达式可以让代码更简洁这个我非常认同,但是各种网络资料上说它更易于阅读和理解我是不认同的。因为使用了lambda一般你反而很难理解这个函数的入参是什么,出参又是什么。比如下面两种方式:
CharacterProcessor a = new CharacterProcessor() {
@Override
public String doProcess(String source) {
return source.concat("abc");
}
};
CharacterProcessor g = v -> v.toUpperCase();
实现上第二种确实很简洁,但是单从理解上显然第一种更容易,第二种要是没有一定的lambda基础一时半会都不知道咋回事,特别是对于新手来说理解起来特费劲。其次就是调试的时候你需要添加断点,那上面的第一种显然更方便断点调试。
猜你喜欢
- 2024-11-30 Java开发「函数式编程」——Lambda表达式
- 2024-11-30 玩转java8 lambda表达式三之对象方法引用
- 2024-11-30 Java 中的 Supplier:让数据生成更灵活
- 2024-11-30 Java 中的 Predicate:让判断逻辑更清晰灵活
- 2024-11-30 Java8精华-函数式编程(一)读完这篇,你将彻底理解
- 2024-11-30 java8精华-函数式编程-Predicate(四)
- 2024-11-30 巧妙应用 Java 的 Functional Interface:让代码更简洁、更灵活
- 2024-11-30 函数式接口编程真没那么难,简单几行让你的代码更优雅
- 2024-11-30 初窥函数式接口,不会取标题,没有噱头,全是干货
- 2024-11-30 JDK8新特性:函数式接口@FunctionalInterface
- 最近发表
- 标签列表
-
- gitpush (61)
- pythonif (68)
- location.href (57)
- tail-f (57)
- pythonifelse (59)
- deletesql (62)
- c++模板 (62)
- css3动画 (57)
- c#event (59)
- linuxgzip (68)
- 字符串连接 (73)
- nginx配置文件详解 (61)
- html标签 (69)
- c++初始化列表 (64)
- exec命令 (59)
- canvasfilltext (58)
- mysqlinnodbmyisam区别 (63)
- arraylistadd (66)
- node教程 (59)
- console.table (62)
- c++time_t (58)
- phpcookie (58)
- mysqldatesub函数 (63)
- window10java环境变量设置 (66)
- c++虚函数和纯虚函数的区别 (66)