函数式接口 lambda表达式 方法引用
拉呱: 终于,学习jdk8的新特性了,初体验带给我的感觉真爽,代码精简的不行,可读性也很好,而且,spring5也是把jdk8的融入到血液里,总之一句话吧,说的打趣一点,学的时候自己难受,学完了写出来的代码,别人看着难受
开篇说一个问题,jdk8是如何把这些新的特性添加进来,并且兼容jdk7及以前版本的?
大家都知道,java的体系的建立,和interface有着莫大的关系,先有接口确定出一套明确的体系,再有它的实现类实现这套体系,比如,超级典型的java里面的集合体系;
新的需求来了,总不能去原有的接口里面添加抽象方法吧? 那不是开玩笑? 接口一改,所有的实现类,全部不能用了! java8是怎么做的呢? 允许接口中添加 default方法, 允许方法存在方法体
- java的拓展接口的方法是 default方法 + 函数式接口 (更进一步说,是default方法的入参大多是该函数式接口的引用,函数体都是基于抽象方法的一套逻辑组合)
仔细想想,还真的是很精妙的,default方法虽然有方法体,但是它们的动作其实是动态传递进去的!!!
可以看一下下面的代码
list.forEach(new Consumer<String>() {@Overridepublic void accept(String integer) {System.out.println(integer);}
});/*** 用lambda表达式的方法实现,得到Consumer的实现* Consumer唯一未实现的抽象方法就是accept -- 接受一个参数,不返回任何值* 下面的i为什么不写类型? 可以看看上面匿名内部类的实现方式, 编译器通过类型推断可以推断出 i 就是integer类型的*/
list.forEach(i->System.out.println(i));
Consumer c1 = i->{};/*** 通过方法引用创建 函数式接口的实例* 鼠标放到 :: 上,点进去, 编译器跳转到了 Consumer函数式接口 , 同样是通过类型推断,内部迭代出每个元素*/
list.forEach(System.out::println);
Consumer c = System.out::println;
- java的拓展类的方法是 类的话,直接添加新的方法就行
但是,相当一部分新增的方法,入参类型,依然是函数式接口
什么是函数式接口呢?
位于 java.util.function包
函数式接口本质上就是个接口,性质如下:
- 如果一个接口只有一个抽象方法,无论有没有FunctionInterface注解,这个接口就是一个函数式接口
- 如果我们在接口上加上了FunctionInterface注解,那么编译器按照函数式接口的要求,处理我们的接口
进一步,对于函数式接口,如何实现它呢?
- lambda表达式
- 方法引用
- 构造方法引用实现对应的实例
- 写个类,实现它, 不过没人这么做,傻里吧唧的
再进一步lambda表达式是什么?
- lambda表达式其实是对象,但是这种对象必须依附于函数式接口
- but,即便我们知道lambda是对象,它到底是什么类型的对象? 只能通过给定的特定的上下文得知
Consumer consumer = i->{};
有啥用?
- 它解决了,在java中我们无法将函数作为参数传递给一个方法,也不能声明一个返回函数的方法这样一个问题
- 像js这种函数编程语言,它当然可以做到,ajax向后端发送请求,得到的返回结果就是一个 回调函数 callback(){}
常见的函数式接口:
jdk8新添加的函数式接口有几十个,但是套路相似,通过下面集合常见的函数式接口,可以搞清楚它的来龙去脉
Consumer
// 接收一个参数,无返回值
void accept(T t);
Function
@FunctionalInterface
public interface Function<T, R> {/*** 接受一个参数,返回一个值* @param t the function argument* @return the function result*/
R apply(T t);
观看下面四行行代码
/** 下面分别用 方法引用 和 lmabda表达式 实现函数式接口Function* Function的函数式方法是apply 接受一个参数,并返回返回值, 这两部分的泛型是Function传递给它的* 右半部分,不管使用什么方法,必须满足两件事,,编译器才会任务他是对函数式方法apply的实现* 1. 方法返回值必须是String(第二个泛型)* 2. 第一个String是使用方法的对象的类型,也必须是string对比这两行代码,你就可以看到,动作是动态传递进去的!~~而不是使用预先定义的行为* */
Function<String,String> function2 = String::toLowerCase;
Function<String,String> function3 = i->i.toUpperCase();// 错误实例
// 无返回值
Function<String,String> function4 = i-> System.out.println(i);// 返回值是布尔类型
Function<String,String> function1 = String::contains;
Function的其他两个方法(详细记录第一个方法的使用)
- 首先,compose()接受一个Function函数接口 before
- 具体执行的过程是 this.apply(before.apply(v)), 也就说,先执行传递进来的这个函数式接口实例的apply方法
- before执行apply(v),他的入参是V ==> ? super V ; 由他可知,这两个apply处理的都是V类型的数据
- 它的返回值是 ? extends T , T就是这个Function唯一接受的参数的类型
- 返回去看apply方法 : R apply(T t); 正好before执行完事把T类型的结果扔给this.apply(),再一步执行apply()
- 最后看完整的看一下 return的形式: return (V v) -> apply(before.apply(v));画重点!!!,return 的这个结果,从形式上看,首先它是个lambda表达式,还可以把它理解成Function唯一的函数式接口的实现(只不过他们接受的参数比较特别),这里也可以直接把它理解成是一个Function类型的实例,或者是一套模板,apply()的嵌套;但是别忘了,apply嵌套的再多,最终也是要处理V, V具体是几? 我们要动态的传递给它,怎么传给他? 用compose()方法的返回值调用apply()方法;
其实上面的流程是java8已经搭好的架子, 我们要做的其实是apply的具体实现,apply是函数式接口,如何实现? lambda表达式,方法引用,构造方法引用随便挑
/*** @param t the function argument* @return the function result*/
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {Objects.requireNonNull(before);return (V v) -> apply(before.apply(v));
}default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {Objects.requireNonNull(after);return (T t) -> after.apply(apply(t));
}
使用的demo
public int compute(int a, Function<Integer,Integer> function1,Function<Integer,Integer> function2){Function<Integer,Integer> fun = function1.compose(function2);fun.apply(a);return function1.compose(function2).apply(a);
}public static void main(String[] args) {FunctionText functionText = new FunctionText();System.out.println(functionText.compute(2,i->i*4,j->j*3));
}
结果是两个24;没差
Bifurcation
- 和Function相似,只不过,它唯一的函数式接口可以接受两个参数,返回一个值
- 它只有andThen()这么一个默认方法,andThen()和compose正好相反,它先执行this.apply,得到一个返回值,传递非入参位置上的Function的apply() -- 因为它刚好接收一个参数,返回一个结果, 具体对谁进行apply?和我上面的分析雷同
@FunctionalInterface
public interface BiFunction<T, U, R> {/***接受两个参数,返回一个值** @param t the first function argument* @param u the second function argument* @return the function result*/
R apply(T t, U u);/***/
default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {Objects.requireNonNull(after);return (T t, U u) -> after.apply(apply(t, u));
}
}
使用Bifurcation的demo
public int add(Integer a, Integer b, BiFunction<Integer,Integer,Integer> biFunction){return biFunction.apply(a,b);
}
public int compute3(Integer a, Integer b,BiFunction<Integer,Integer,Integer> biFunction,Function<Integer,Integer> function){return biFunction.andThen(function).apply(a,b);
}System.out.println(functionText.add(1,2,( a , b ) -> a + b));
System.out.println(functionText.compute3(3,4,(c,d)->c*d,i->i+1));
Predicate
用于动态的判断传递给他的类型是否相等
学习过上面那几个函数式接口,再看它,应该是很容易蒙出怎么玩了
- 套路: 到现在看,他和上面几个函数式接口的套路还是大同小异的, java8针对不同的使用情景设计出不同的函数式接口,Predicate意味,断定,判相等
- 下面的三个默认方法,入参全部是Predicate类型的形参,目的是和当前对象的text()结合形成多重判断
- 最后一个static静态方法,使用的是静态方法的引用
- 关于我们: 我们能做的依旧是写出lambda表达式,作为text的真正的业务逻辑
@FunctionalInterface
public interface Predicate<T> {boolean test(T t);default Predicate<T> and(Predicate<? super T> other) {Objects.requireNonNull(other);return (t) -> test(t) && other.test(t);
}default Predicate<T> negate() {return (t) -> !test(t);
}default Predicate<T> or(Predicate<? super T> other) {Objects.requireNonNull(other);return (t) -> test(t) || other.test(t);
}static <T> Predicate<T> isEqual(Object targetRef) {return (null == targetRef)? Objects::isNull: object -> targetRef.equals(object);
}
}
不接受参数,但是返回一个值
方法引用
方法引用其实是lambda的语法糖,当我们的lambda表达式只有一行并且恰好有一已经存在的方法作用跟他相同,我们就可以用方法引用替换lambda表达式,让代码的风格更好看
四类方法引用
一方面编译器会提示如何使用方法引用,另一方面,我们自己要根据方法的类型知道如何引用
- 类名::静态方法名
- 引用名(对象名)::实例方法名
- 类名::实例方法名
lambda表示的第一个参数是作为方法的调用者传递进去的
- 类名::new --- 构造方法引用
编译可以很智能的推断出,你在使用哪个构造方法
public class text1 {public String getString1(String str, Function<String,String> function){return function.apply(str);
}
public String getString2(String str, Supplier<String> function){return function.get();
}public static void main(String[] args) {text1 text1 = new text1();String haha = text1.getString1("haha", String::new);System.out.println(haha);String hehe = text1.getString2("hehe", String::new);System.out.println(hehe);}
通过类名去找到对象的实例方法
转载于:https://www.cnblogs.com/ZhuChangwu/p/11150567.html
函数式接口 lambda表达式 方法引用相关推荐
- 学习笔记之-java8的新特性-函数式接口,lambda表达式,方法引用,Stream API,Optional类
1.Lambda表达式 用匿名内部类的方法去创建多线程1.new Thread2.参数传递new Runnable3.重写run方法4.在run方法中去设置线程任务5.调用start问题:我们最终目标 ...
- 三、jdk1.8新特新Lambda表达式方法引用
前言 在之前我们接触了JDK1.8引入的新特新lambda表达式没在某种程度上,它可以简化我们的代码,帮助我们快速的编写代码,但在这其中我们之前的编写方式并不是lambda表达式最简洁的方式,而在头屑 ...
- Java Lambda(语言篇——lambda,方法引用,目标类型,默认方法,函数接口,变量捕获)
深入理解Java 8 Lambda(语言篇--lambda,方法引用,目标类型和默认方法) 原文链接:http://zh.lucida.me/blog/java-8-lambdas-insideout ...
- 深入理解Java 8 Lambda(语言篇——lambda,方法引用,目标类型和默认方法)
作者:Lucida 微博:@peng_gong 豆瓣:@figure9 原文链接:http://zh.lucida.me/blog/java-8-lambdas-insideout-language- ...
- [转]深入理解Java 8 Lambda(语言篇——lambda,方法引用,目标类型和默认方法)...
以下内容转自: 作者:Lucida 微博:@peng_gong 豆瓣:@figure9 原文链接:http://zh.lucida.me/blog/java-8-lambdas-insideout-l ...
- 【我的ASM学习进阶之旅】ASM 实现 Hook Lambda 和方法引用
ASM 实现 Hook Lambda 和方法引用 | 数据采集 PS: 本文转载于:ASM 实现 Hook Lambda 和方法引用 | 数据采集 1. 前言 本文是前作「Lambda 设计参考」的实 ...
- 函数式编程 lambda表达式
函数式编程 lambda表达式 从JDK1.8之后为了简化程序的开发,专门提供了lambda表达式的支持,可以简化编程中接口的复杂操作: 范例: 观察以下传统开发中的问题 public interfa ...
- Java learn lambda的方法引用
lambda 的方法引用 实质也是建立在把引用方法的代码段简写成lambda表达式 分为以下几种: 1.引用静态方法 首先得有个静态方法 写成静态方法是因为 main主方法用类名调用时只会调用静态方法 ...
- lambda表达式方法泛型_模板方法模式–使用Lambda表达式,默认方法
lambda表达式方法泛型 模板方法模式是Erich Gamma,Richard Helm,Ralph Johnson和John Vlissides在著名的< 设计模式>一书中解释的23种 ...
最新文章
- CUDA Samples: image normalize(mean/standard deviation)
- 网络服务-RSYNC
- linux yum配置文件 yum.conf 简介
- Nginx负载均衡实现之四层与七层负载
- pip install mysql-connector 安装出错
- 学习Asp.Net经常会用到的函数集
- 数据中台和业务中台的区别
- SQL优化之not in
- 对select标签中的option默认选中后端的数据
- 一位技术主管的十年编程经验总结
- 利用NMDS对药物处理下肠道菌群微生物群落多态性分析
- 游戏服务器租用阿里云和腾讯云价格对比
- HDU 4269 Defend Jian Ge 解题报告
- 服务器虚拟化双活,分布式双活数据中心部署模式
- ubuntu14.04安装krita
- 仙剑游戏系列..感想
- 浮华的世态,只会将一颗心,涂染得色彩缤纷,失去往日纯净的姿态
- 顾维维就任百度新兴业务及技术体系市场负责人
- IRIS Docker的安装
- 软考(软件设计师)考点总结 -- 超详细整理
热门文章
- iis8使用url2.0模块实现http跳转到https
- 阿里云的RDS 查看binlog日志的方法
- 策略模式Strategy——坐什么车回家?
- Modular Java
- java 非法线程_JVM中的线程行为
- 台达plc读取变频器电流案例_MODBUS通信之触摸屏与变频器通信知识分享(一)
- 线性回归 php,PHP实现简单线性回归之数学库的重要性
- html计算天数,Javascript实现简易天数计算器
- 哈理工计算机学院保研,哈尔滨理工大学计算机科学与技术学院(专业学位)软件工程保研夏令营...
- messagebox java_如何从messagebox获得答案