那些年,我们追过的java8
9月份java9就要发布了,在8月的最后一天决定回顾一下java8那些惊天动地的变化,加深理解,共同进步。
我们都知道java与c++,c不同是一个为面向对象而生的语言,面向对象思想贯彻了java发展的大部分时间直到java8,java8的出现为java引进了新的思想(虽然这个思想在别的语言里早就有了)--函数式编程,这是两种思想的碰撞,导致刚刚接触java8会觉得自己不是在写java代码。。。好了废话不多说,先总结java8的一些颠覆性的变动点:
1.接口允许有实现:
public interface MyInterface {
default String getResult(){
return "hello world!";
}
}
这叫默认实现,据说是为了foreach而引进的,默认方法实现带来了另一个问题--多重继承,但是又不是完全意义上的多重继承,因为继承的是方法,相当于无状态多重继承。所以必须有一些规则来处理多重继承所带来的问题:
- 继承最近的接口的默认方法。
- 使用重写后的方法
- 当使用以上规则后仍无法确认时,会报错
2.集合
java8对集合的处理才是重头戏。先来看看几个常见的函数式接口:
Consumer
Supplier
Operator
Function
以及在此基础上的一些扩展
BiConsumer
BinrayOperator
BiFunction
接口就不一一介绍,可以看一下源码,也很简单,就是定义了函数的处理方式抽象了各种不同的函数
集合中最常用的工具类:Collectors是本次分析的主角。
在看Collectors类之前,必须先看看一个接口:Collector
它有三个类型:入参类型,中间类型,出参类型,这个接口是干什么的呢,通过它定义的参数,我们基本上可以猜到,它定义了一系列函数,将入参经过一系列处理,变化,得到另一个结果。源码上有一系列注释,说明了这个类的作用,这里简单翻译一下:
Collector是一个由4个函数组成的,用来做相当于reduce的惰性求值的类。这4个函数是:
Supplier<A> supplier();--用来生成存放惰性求值结果的容器
BiConsumer<A, T> accumulator();--用来计算入参,并将结果放入容器的函数
BinaryOperator<A> combiner();--用来将两个容器合并成一个容器。(这个是一个fork/join的一个重要步骤)
Function<A, R> finisher();--将容器中结果转化为想要的结果的函数。
其实它还有第5个参数,表明COllector是线程安全的还是不安全的或者A就是R不需要最后一步。 Collector就介绍到这里,在Collectors里有Collector的默认实现。 Collectors里面定义了诸多工具方法,不过,为了展示函数式编程的魅力,我们挑一个长一点的出来分析一把:groupingBy方法,源码如下:
public static <T, K, D, A, M extends Map<K, D>>
Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier,
Supplier<M> mapFactory,
Collector<? super T, A, D> downstream) {
Supplier<A> downstreamSupplier = downstream.supplier(); //获取downstream的容器函数
BiConsumer<A, ? super T> downstreamAccumulator = downstream.accumulator(); //获取downstream的reduce操作的计算函数
BiConsumer<Map<K, A>, T> accumulator = (m, t) -> { //将计算结果放入map中的函数(也是新的collector的计算函数)
K key = Objects.requireNonNull(classifier.apply(t), "element cannot be mapped to a null key");
A container = m.computeIfAbsent(key, k -> downstreamSupplier.get()); //这步是关键,通过map的key来判断是不是同一类,如果是同一类,则返回原来的容器,否则则使用downstream的函数创建一个容器
downstreamAccumulator.accept(container, t); //为容器填充值
};
BinaryOperator<Map<K, A>> merger = Collectors.<K, A, Map<K, A>>mapMerger(downstream.combiner()); //获取合并函数
@SuppressWarnings("unchecked")
Supplier<Map<K, A>> mangledFactory = (Supplier<Map<K, A>>) mapFactory; //该函数返回结果容器函数(Map类型) if (downstream.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)) { //如果downstream的类型是IDENTITY_FINISH那么返回新的collector
return new CollectorImpl<>(mangledFactory, accumulator, merger, CH_ID);
}
else {
@SuppressWarnings("unchecked")
Function<A, A> downstreamFinisher = (Function<A, A>) downstream.finisher(); //否则获取计算结果的函数
Function<Map<K, A>, M> finisher = intermediate -> { //生成新的结果函数
intermediate.replaceAll((k, v) -> downstreamFinisher.apply(v));
@SuppressWarnings("unchecked")
M castResult = (M) intermediate;
return castResult;
};
return new CollectorImpl<>(mangledFactory, accumulator, merger, finisher, CH_NOID);
}
}
先看注释中的一段例子:
Map<Department, Integer> totalByDept
= employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment,
Collectors.summingInt(Employee::getSalary)));
这个例子是计算公司每个部门员工工资综合,具体的pojo就不给不来了。
纵观整个过程,groupingBy将downstream(Collector)(直译:下游流)按照classfier的规则分类,将结果放入由mapFactory函数生成的Map容器中,返回新的Collector。我们看到,如何由一个老的Collector经过一系列处理,变为新的Collector,其实至此仍未结束,我们仍可以对
新的Collector做更多操作,这些操作都不会发生,直到我们调用了及早求值的方法,真正的操作才会发生,如上面的stream.collect,函数式编程操作的不再是数据,而是一个个函数,可以看做一个函数对另一个函数的装饰从而形成新的函数。
总之,函数式编程已经不仅仅是思想,也是一种编程习惯,如果我们不用,不写,即使说再多,也是纸上谈兵。
java8还是要更多练习与探索的。
java9迎接你的到来。
那些年,我们追过的java8的更多相关文章
- Java8学习笔记(九)--日期/时间(Date Time)API指南
Java 8日期/时间( Date/Time)API是开发人员最受追捧的变化之一,Java从一开始就没有对日期时间处理的一致性方法,因此日期/时间API也是除Java核心API以外另一项倍受欢迎的内容 ...
- (转载)Java8新的日期API LocalDate, LocalTime
前言 由于Java Date的各种问题,Java8推出了新的日期API,很受一拨人的追捧. 为什么我们需要新的Java日期/时间API? 在开始研究Java 8日期/时间API之前,让我们先来看一下为 ...
- java8之流的基本使用(二)
概述 流(stream())是java8的一个新特性,主要的作用就是将各种类型的集合转换为流,然后的方便迭代数据用的.例如: //将List类型的集合转换为流 list.stream() 转换为流之后 ...
- [转] Java8 日期/时间(Date Time)API指南
[From] http://www.importnew.com/14140.html Java 8日期/时间( Date/Time)API是开发人员最受追捧的变化之一,Java从一开始就没有对日期时间 ...
- Java8实战分享
虽然很多人已经使用了JDK8,看到不少代码,貌似大家对于Java语言or SDK的使用看起来还是停留在7甚至6. Java8在流式 or 链式处理,并发 or 并行方面增强了很多,函数式的风格使代码可 ...
- java8中lambda表达式的应用,以及一些泛型相关
语法部分就不写了,我们直接抛出一个实际问题,看看java8的这些新特性究竟能给我们带来哪些便利 顺带用到一些泛型编程,一切都是为了简化代码 场景: 一个数据类,用于记录职工信息 public clas ...
- Android Studio2.1.2 Java8环境下引用Java Library编译出错
转载请注明出处:http://www.cnblogs.com/LT5505/p/5685242.html 问题:在Android Studio2.1.2+Java8的环境下,引用Java Librar ...
- Java笔记——Java8特性之Lambda、方法引用和Streams
Java8已经推出了好一段时间了,而掌握Java8的新特性也是必要的,如果要进行Spring开发,那么可以发现Spring的官网已经全部使用Java8来编写示例代码了,所以,不学就看不懂. 这里涉及三 ...
- 关于Java8函数式编程你需要了解的几点
函数式编程与面向对象的设计方法在思路和手段上都各有千秋,在这里,我将简要介绍一下函数式编程与面向对象相比的一些特点和差异. 函数作为一等公民 在理解函数作为一等公民这句话时,让我们先来看一下一种非常常 ...
随机推荐
- k8s环境部署.net core web项目(docker本地仓库)
在之前的文档中,我们部署了.net core web在k8s环境下,达成了集群管理项目的目的.但是,由于是本地部署,需要在所有的node节点都拉取好镜像,这是非常麻烦的,为了只维护一份代码,同步更新. ...
- 手写node可读流之流动模式
node的可读流基于事件 可读流之流动模式,这种流动模式会有一个"开关",每次当"开关"开启的时候,流动模式起作用,如果将这个"开关"设置成 ...
- 关于org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.atguigu.crud.dao.DepartmentMapper.insertSelective的错误
今天我在使用mybatis逆向工程的时候,由于一个疏忽字打错了..结果花了一早上才把错误找全..广大小伙伴们一定要小心啊(能复制粘贴就别手打) 关于org.apache.ibatis.binding. ...
- Spring Boot从入门到精通(七)集成Redis实现Session共享
单点登录(SSO)是指在多个应用系统中,登录用户只需要登录验证一次就可以访问所有相互信任的应用系统,Redis Session共享是实现单点登录的一种方式.本文是通过Spring Boot框架集成Re ...
- mysql从5.5升级到5.7遇到的坑
在安装mysql5.7时很顺利安装完成,但在启动项目时报错: [Err] 1055 - Expression #1 of ORDER BY clause is not in GROUP BY clau ...
- C#桌面开发的未来-WebWindow
WebWindow源码作者博客基于Chromium的Edge体验体验方式一:体验方式二:预期目标:遗留的问题 WebWindow WebWindow是跨平台的库. Web Window的当前实验实现可 ...
- 使用synchronized修饰静态方法和非静态方法有什么区别
前言 最近被问到了这个问题,第一次回答的也是很不好,在此参考网上答案进行整理记录.供大家学习参考. Synchronized修饰非静态方法 Synchronized修饰非静态方法,实际上是对调用该方法 ...
- pytest、tox、Jenkins实现python接口自动化持续集成
pytest介绍 pytest是一款强大的python测试工具,可以胜任各种级别的软件测试工作,可以自动查找测试用并执行,并且有丰富的基础库,可以大幅度提高用户编写测试用例的效率,具备可扩展性,用户自 ...
- vue — 创建vue项目
创建vue项目 在程序开发中,有三种方式创建vue项目,本地引入vuejs.使用cdn引入vuejs.使用vue-cli创建vue项目.其中vue-cli可以结合webpack打包工具使用,大大方便了 ...
- MAC下安装Fiddler抓包工具
需求 我们都知道在Mac电脑下面有一个非常好的抓包工具:Charles.但是这个只能抓代理的数据包.但是有时候想要调试本地网卡的数据库 Charles 就没办法了.就想到了在windows下面的一个F ...