java中用方法重写求最大值_java8 函数式编程一

一、函数接口

接口

参数

返回类型

描述

predicate

t

boolean

用来比较操作

consumer

t

void

没有返回值的函数

function

t

r

有返回值的函数

supplier

none

t

工厂方法-返回一个对象

unaryoperator

t

t

入参和出参都是相同对象的函数

binaryoperator

(t,t)

t

求两个对象的操作结果

为什么要先从函数接口说起呢?因为我觉得这是 java8 函数式编程的入口呀!每个函数接口都带有 @functionalinterface 注释,有且仅有一个未实现的方法,表示接收 lambda 表达式,它们存在的意义在于将代码块作为数据打包起来。

没有必要过分解读这几个函数接口,完全可以把它们看成普通的接口,不过他们有且仅有一个抽象方法(因为要接收 lambda 表达式啊)。

@functionalinterface 该注释会强制 javac 检查一个接口是否符合函数接口的标准。 如果该注释添加给一个枚举类型、 类或另一个注释, 或者接口包含不止一个抽象方法, javac 就会报错。

二、lambda 表达式

1、lambda 表达式和匿名内部类

先来复习一下匿名内部类的知识:

如果是接口,相当于在内部返回了一个接口的实现类,并且实现方式是在类的内部进行的;

如果是普通类,匿名类相当于继承了父类,是一个子类,并可以重写父类的方法。

需要特别注意的是,匿名类没有名字,不能拥有一个构造器。如果想为匿名类初始化,让匿名类获得一个初始化值,或者说,想使用匿名内部类外部的一个对象,则编译器要求外部对象为final属性,否则在运行期间会报错。

new thread(new runnable() {

@override

public void run() {

system.out.println(123);

}

}).start();

new thread(()-> system.out.println(123)).start();

如上,和传入一个实现某接口的对象不同, 我们传入了一段代码块 —— 一个没有名字的函数。() 是参数列表, 和上面匿名内部类示例中的是一样的。 -> 将参数和 lambda 表达式的主体分开, 而主体是之后操作会运行的一些代码。

lambda 表达式简化了匿名内部类的写法,省略了函数名和参数类型。即参数列表 () 中可以仅指定参数名而不指定参数类型。

java 是强类型语言,为什么可以不指定参数类型呢?这得益于 javac 的类型推断机制,编译器能够根据上下文信息推断出参数的类型,当然也有推断失败的时候,这时就需要手动指明参数类型了。javac 的类型推断机制如下:

对于类中有重载的方法,javac 在推断类型时,会挑出最具体的类型。

如果只有一个可能的目标类型, 由相应函数接口里的参数类型推导得出;

如果有多个可能的目标类型, 由最具体的类型推导得出;

如果有多个可能的目标类型且最具体的类型不明确, 则需人为指定类型。

2、lambda 表达式和集合

java8 在 java.util 包中引入了一个新的类 —— stream.java。java8 之前我们迭代集合,都只能依赖外部迭代器 iterator 对集合进行串行化处理。而 stream 支持对集合顺序和并行聚合操作,将更多的控制权交给集合类,是一种内部迭代方式。这有利于方便用户写出更简单的代码,明确要达到什么转化,而不是如何转化。

stream 的操作有两种,一种是描述 stream ,如 filter、map 等最终不产生结果的行为称为"惰性求值";另外一种像 foreach、collect 等是从 stream 中产生结果的行为称为"及早求值"。

接下来让我们瞧瞧 stream 如何结合 lambda 表达式优雅的处理集合...

foreach - 迭代集合

list.foreach(e -> system.out.println(e));

map.foreach((k, v) -> {

system.out.println(k);

system.out.println(v);

});

collect(tolist()) - 由stream里的值生成一个列表。

list list = stream.of("java", "c++", "python").collect(collectors.tolist());

等价于:

list aslist = arrays.aslist("java", "c++", "python");

filter - 遍历并检查过滤其中的元素。

long count = list.stream().filter(x -> "java".equals(x)).count();

map、maptoint、maptolong、maptodouble - 将流中的值转换成一个新的值。

list maplist = list.stream().map(str -> str.touppercase()).collect(collectors.tolist());

list list = stream.of("java", "javascript", "python").collect(collectors.tolist());

intsummarystatistics intsummarystatistics = list.stream().maptoint(e -> e.length()).summarystatistics();

system.out.println("最大值:" + intsummarystatistics.getmax());

system.out.println("最小值:" + intsummarystatistics.getmin());

system.out.println("平均值:" + intsummarystatistics.getaverage());

system.out.println("总数:" + intsummarystatistics.getsum());

maptoint、maptolong、maptodouble 和 map 操作类似,只是把函数接口的返回值改为 int、long、double 而已。

flatmap - 将多个 stream 连接成一个 stream

list streamlist = stream.of(list, aslist).flatmap(x -> x.stream()).collect(collectors.tolist());

flatmap 方法的相关函数接口和 map 方法的一样, 都是 function 接口, 只是方法的返回值限定为 stream 类型罢了。

max-求最大值、min-求最小值

string maxstr = list.stream().max(comparator.comparing(e -> e.length())).get();

string minstr = list.stream().min(comparator.comparing(e -> e.length())).get();

reduce - 聚合操作,从一组元素中生成一个值,sum()、max()、min()、count() 等都是reduce操作,将他们单独设为函数只是因为常用。

integer sum1 = stream.of(1, 2, 3).reduce(0, (acc, e) -> acc + e);

上述执行求和操作,有两个参数: 传入 stream 中初始值和 acc。 将两个参数相加,acc 是累加器,保存着当前的累加结果。

待续...

三、默认方法

java8 中新增了 stream 操作,那么第三方类库中的自定义集合 mylist 要怎么做到兼容呢?总不能升级完 java8,第三方类库中的集合实现全都不能用了吧?

为此,java8 在接口中引入了"默认方法"的概念!默认方法是指接口中定义的包含方法体的方法,方法名有 default 关键字做前缀。默认方法的出现是为了 java8 能够向后兼容。

public interface iterable {

/**

* performs the given action for each element of the {@code iterable}

* until all elements have been processed or the action throws an

* exception. unless otherwise specified by the implementing class,

* actions are performed in the order of iteration (if an iteration order

* is specified). exceptions thrown by the action are relayed to the

* caller.

*

* @implspec

*

the default implementation behaves as if:

*

{@code

* for (t t : this)

* action.accept(t);

* }

*

* @param action the action to be performed for each element

* @throws nullpointerexception if the specified action is null

* @since 1.8

*/

default void foreach(consumer super t> action) {

objects.requirenonnull(action);

for (t t : this) {

action.accept(t);

}

}

}

看 java8 中的这个 iterable.java 中的默认方法 foreach(consumer super t> action),表示“如果你们没有实现 foreach 方法,就使用我的吧”。

默认方法除了添加了一个新的关键字 default,在继承规则上和普通方法也略有差别:

类胜于接口。如果在继承链中有方法体或抽象的方法声明,那么就可以忽略接口中定义的方法。

子类胜于父类。果一个接口继承了另一个接口, 且两个接口都定义了一个默认方法,那么子类中定义的方法胜出。

如果上面两条规则不适用, 子类要么需要实现该方法, 要么将该方法声明为抽象方法。

四、其他

使用 lambda 表达式,就是将复杂性抽象到类库的过程。

面向对象编程是对数据进行抽象, 而函数式编程是对行为进行抽象。

java8 虽然在匿名内部类中可以引用非 final 变量, 但是该变量在既成事实上必须是final。即如果你试图给该变量多次赋值, 然后在 lambda 表达式中引用它, 编译器就会报错。

stream 是用函数式编程方式在集合类上进行复杂操作的工具。

无论何时,将 lambda 表达式传给 stream 上的高阶函数,都应该尽量避免副作用。唯一的例外是 foreach 方法,它是一个终结方法。没有副作用指的是:函数不会改变程序或外界的状态。

对于需要大量数值运算的算法来说, 装箱和拆箱的计算开销, 以及装箱类型占用的额外内存, 会明显减缓程序的运行速度。为了减小这些性能开销, stream 类的某些方法对基本类型和装箱类型做了区分。比如 intstream、longstream 等。

java8 对为 null 的字段也引进了自己的处理,既不用一直用 if 判断对象是否为 null,来看看?

public static list getassistant(long tenantid) {

// ofnullable 如果 value 为null,会构建一个空对象。

optional> assistantvo = optional.ofnullable(assistant_map.get(tenantid));

// orelse 如果 value 为null,选择默认对象。

assistantvo.orelse(assistant_map.get(default_tenant));

return assistantvo.get();

}

希望与广大网友互动??

点此进行留言吧!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值