拉呱: 终于,学习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包

函数式接口本质上就是个接口,性质如下:

  1. 如果一个接口只有一个抽象方法,无论有没有FunctionInterface注解,这个接口就是一个函数式接口
  2. 如果我们在接口上加上了FunctionInterface注解,那么编译器按照函数式接口的要求,处理我们的接口

进一步,对于函数式接口,如何实现它呢?

  1. lambda表达式
  2. 方法引用
  3. 构造方法引用实现对应的实例
  4. 写个类,实现它, 不过没人这么做,傻里吧唧的

再进一步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表达式 方法引用相关推荐

  1. 学习笔记之-java8的新特性-函数式接口,lambda表达式,方法引用,Stream API,Optional类

    1.Lambda表达式 用匿名内部类的方法去创建多线程1.new Thread2.参数传递new Runnable3.重写run方法4.在run方法中去设置线程任务5.调用start问题:我们最终目标 ...

  2. 三、jdk1.8新特新Lambda表达式方法引用

    前言 在之前我们接触了JDK1.8引入的新特新lambda表达式没在某种程度上,它可以简化我们的代码,帮助我们快速的编写代码,但在这其中我们之前的编写方式并不是lambda表达式最简洁的方式,而在头屑 ...

  3. Java Lambda(语言篇——lambda,方法引用,目标类型,默认方法,函数接口,变量捕获)

    深入理解Java 8 Lambda(语言篇--lambda,方法引用,目标类型和默认方法) 原文链接:http://zh.lucida.me/blog/java-8-lambdas-insideout ...

  4. 深入理解Java 8 Lambda(语言篇——lambda,方法引用,目标类型和默认方法)

    作者:Lucida 微博:@peng_gong 豆瓣:@figure9 原文链接:http://zh.lucida.me/blog/java-8-lambdas-insideout-language- ...

  5. [转]深入理解Java 8 Lambda(语言篇——lambda,方法引用,目标类型和默认方法)...

    以下内容转自: 作者:Lucida 微博:@peng_gong 豆瓣:@figure9 原文链接:http://zh.lucida.me/blog/java-8-lambdas-insideout-l ...

  6. 【我的ASM学习进阶之旅】ASM 实现 Hook Lambda 和方法引用

    ASM 实现 Hook Lambda 和方法引用 | 数据采集 PS: 本文转载于:ASM 实现 Hook Lambda 和方法引用 | 数据采集 1. 前言 本文是前作「Lambda 设计参考」的实 ...

  7. 函数式编程 lambda表达式

    函数式编程 lambda表达式 从JDK1.8之后为了简化程序的开发,专门提供了lambda表达式的支持,可以简化编程中接口的复杂操作: 范例: 观察以下传统开发中的问题 public interfa ...

  8. Java learn lambda的方法引用

    lambda 的方法引用 实质也是建立在把引用方法的代码段简写成lambda表达式 分为以下几种: 1.引用静态方法 首先得有个静态方法 写成静态方法是因为 main主方法用类名调用时只会调用静态方法 ...

  9. lambda表达式方法泛型_模板方法模式–使用Lambda表达式,默认方法

    lambda表达式方法泛型 模板方法模式是Erich Gamma,Richard Helm,Ralph Johnson和John Vlissides在著名的< 设计模式>一书中解释的23种 ...

最新文章

  1. CUDA Samples: image normalize(mean/standard deviation)
  2. 网络服务-RSYNC
  3. linux yum配置文件 yum.conf 简介
  4. Nginx负载均衡实现之四层与七层负载
  5. pip install mysql-connector 安装出错
  6. 学习Asp.Net经常会用到的函数集
  7. 数据中台和业务中台的区别
  8. SQL优化之not in
  9. 对select标签中的option默认选中后端的数据
  10. 一位技术主管的十年编程经验总结
  11. 利用NMDS对药物处理下肠道菌群微生物群落多态性分析
  12. 游戏服务器租用阿里云和腾讯云价格对比
  13. HDU 4269 Defend Jian Ge 解题报告
  14. 服务器虚拟化双活,分布式双活数据中心部署模式
  15. ubuntu14.04安装krita
  16. 仙剑游戏系列..感想
  17. 浮华的世态,只会将一颗心,涂染得色彩缤纷,失去往日纯净的姿态
  18. 顾维维就任百度新兴业务及技术体系市场负责人
  19. IRIS Docker的安装
  20. 软考(软件设计师)考点总结 -- 超详细整理

热门文章

  1. iis8使用url2.0模块实现http跳转到https
  2. 阿里云的RDS 查看binlog日志的方法
  3. 策略模式Strategy——坐什么车回家?
  4. Modular Java
  5. java 非法线程_JVM中的线程行为
  6. 台达plc读取变频器电流案例_MODBUS通信之触摸屏与变频器通信知识分享(一)
  7. 线性回归 php,PHP实现简单线性回归之数学库的重要性
  8. html计算天数,Javascript实现简易天数计算器
  9. 哈理工计算机学院保研,哈尔滨理工大学计算机科学与技术学院(专业学位)软件工程保研夏令营...
  10. messagebox java_如何从messagebox获得答案