函数式编程_类型拆装及重载解析
朝臣待漏五更寒,
铁甲将军夜渡关。
山寺日高僧未起,
看来名利不如闲。
一首定场消声去,醒木拍桌开书来。各位看官,别来无恙,今儿个我们聊一番函数式编程的类库。
书归正传,让我们来探一番函数式类库地究竟。在雷记很久之前地帖子中,对于函数式地流做了一些简单的介绍,还有一些基于lambda和stream地使用的例子,让我们继续沿着这个思路搞下去。
要怎样在代码中使用lambda表达式?
由于lambda表达式函数接口的类型可以内部推导,因此在调用角度看来,调用lambda表达式和调用普通的接口方法没有区别。让我们看一个日常处理中十分常见的场景:
公司分派了一个新的项目下来,要处理大数据量的日志,在编码时日志框架中要设置类似void debug(String message)的方法,在判断上下文日志级别为debug时,要打印对应日志级别的日志。那么这时就会出现一个问题,要在各处判断日志级别是否为debug级别,频繁地计算判断会影响系统性能,海量的数据写入也会导致系统io占用率被打到很高。
面对这个情况,要拆分一下问题,首先日志等级判断的频繁性,其次是海量日志的io写,针对这两个问题,第一个可以采用lambda+判定开关降低性能开销,第二个可以采用基于epoll原型的nio方式,第二个问题展开讲在这里会有跑题的嫌疑,所以暂且不表。关于第一个问题,具体处理:
·重写logger类的debug方法:
public void debug(Supplier<String> message) {
if(isDebugEnaled()){
debug(message.get());
}
}
·日志判定采用lambda参数:
Logger log = new Logger();
log.debug(() -> "xxxx");
这里对于Supplier<String> message简单看一下,supplier意为供应商,位于java.util.function.Supplier接口,提供一个get方法获取泛型参数指定类型的对象元素,接口定义为:
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
函数式中对于基本类型、装箱类型的拆装箱处理
在java中有时我们想要一个包含整数型值的List<int>,实际返回得到的却是List<Integer>,原因是java的泛型类型擦除,这个问题进而导致的就是带来对于基本类型进行拆封箱的内存消耗,基本类型和封装类型在内存中的占用字节不同,导致额外开销随着数值运算的复杂度正向增加。
为了减小这类的性能开销,java8中stream对于整型、长整型、双浮点数进行了特殊处理,stream方法中命名带有to或以类型名开头的方法即是,因为经过了改善和优化,所以要尽可能多的使用这类函数,此外它还有很多额外的方法,可以避免我们重复造轮子实现一些通用且容易处bug的方法。
重载解析
java通过不同方法签名实现重载,这对于stream推断类型时会带来问题,导致推断出很多种类型,所以javac会挑出最具体的类型,对此有一个默认的规则:
·只有一个可能的目标类型,由相应函数接口的参数类型推到;
·有多个可能的目标类型,由最具体的类型推导得出;
·有多个可能的目标类型且最具体的类型不明确,需要人为指定类型;
@FunctionalInterface
为提高stream对象可操作性而引入的各类接口,都需要lambda表达式恶意实现它们,这类接口存在的意义是将代码块作为数据打包起来,这正是通过@FunctionalInterface做到的,@FunctionalInterface会强制javac检查一个接口是否符合函数接口标准,如果这个注释加到枚举类、类或另一个注释或接口包含不止一个抽象方法,在javac层面就会报错,使用它在重构代码时能很快发现问题。
结语
最近这段时间因为家庭和公司琐事,导致更新变慢,承蒙各位错爱,惶恐惶恐。后面地更新里,雷记打算尝试加一些vue的前端内容,进而最终过渡到小程序和app制作上,目前是这么打算。如果各位看官有更好的想法,欢迎私信雷记交流,那么今天的更新就是这样,愿诸位,酒色财气君莫占,招财进宝,日进斗金~