java通用函数_通用函数接口java.util.function.*[

【2.1.4 通用函数接口java.util.function.*   返回目录】

为了配合λ表达式的使用,定义了一些作为形参的函数接口。java.util.function包基本覆盖了程序员对函数接口的各种需求。

1.方法的类型签名

函数的类型签名,描述方法的形参列表类型通过本方法处理后,形成返回值类型。以如下格式描述:(形参列表类型) ->返回值类型

从方法的类型签名分析,java.util.function包的核心函数接口有4个。

Ø        函数型T ->R,完成参数类型T向结果类型R的转换。核心函数接口Function

Ø        判断型T ->boolean,核心函数接口Predicate/谓词

Ø        消费型T ->void,核心函数接口Consumer

Ø        供给型void->T,核心函数接口Supplier

其他函数接口与它们类似。各种类型签名可以拓展到二元如(T, U) -> R,三元及更多元的类型签名,难以支持。

由于Java泛型采用擦除技术,Java中不可以用同一个名字定义不同类型或不同数量的参数的泛型类,即无法定义Function、Function、Function等,而必须取不同的类型名字。

2.避免装箱和拆箱

Java泛型仅针对引用类型,如果使用Function,会将代码中的int进行装箱,从而在性能上付出代价。java.util.function包针对基本类型的int、double和long提供支持,当输入或/和输出为基本类型时,可以避免自动装箱的操作。

核心函数接口

简化或二元拓展

基本类型

Function,T ->R

共25个接口

IntFunction,int->R

LongFunction

DoubleFunction

IntToDoubleFunction, int->double

IntToLongFunction

LongToDoubleFunction,

LongToIntFunction,

DoubleToIntFunction

DoubleToLongFunction

ToIntFunction, T->int

ToDoubleFunction,

ToLongFunction

BiFunction,(T,U) ->R

ToIntBiFunction,

ToLongBiFunction,

ToDoubleBiFunction

UnaryOperator, T->T

IntUnaryOperator,

LongUnaryOperator,

DoubleUnaryOperator

BinaryOperator(T,T) ->T

IntBinaryOperator,

LongBinaryOperator,

DoubleBinaryOperator

PredicateT->boolean

共5个接口

IntPredicate,

LongPredicate,

DoublePredicate

BiPredicate(L,R)->boolean

Consumer, T->void

共8个接口

IntConsumer,

LongConsumer,

DoubleConsumer

BiConsumer(T,U)->void

ObjIntConsumer,

ObjLongConsumer,

ObjDoubleConsumer

Supplier()->T

共5个接口

BooleanSupplier,

IntSupplier,

LongSupplier,

DoubleSupplier

3.自定义函数接口

虽然java.util.function包提供了通用函数接口,在后面的例子中仍然使用了自定义函数接口,主要考虑在演示代码中自定义函数接口比通用函数接口更容易理解。某种程度上,使用通用函数接口,如同定义各种类时,将各种类的方法全部命名为doSth()一样,会降低代码的可读性。

另一方面,通过自定义函数接口,可以掌握通用函数接口的使用,这里就不举例说明通用函数接口的用法了。

4.方法引用

函数接口主要支持λ表达式的使用,例如

ToIntFunctionf = s->s.length();

值得注意的是,在λ表达式中调用某个类的方法或构造器的情形,Java 8提供了λ表达式的简化版本,方法引用。

ToIntFunctionf = String::length;

方法引用是否会降低代码的可读性,见仁见智。

Consumerc= System.out::println;

IntConsumer   c2= System.out::println;

当调用accept(),要注意c需要的参数为泛型指出的String,而c2需要的参数为类名IntConsumer暗示的int。

c.accept("hello");

c2.accept(5);

另外,yqj2065觉得冒号冒号,很丑陋。

注:在我们讨论各种函数接口时,关心类型签名,而函数名applyAsDouble等等不需要知道,yqj2065说过:lambda表达式是省略了名字的函数。通常,通用函数接口中抽象方法的名字如applyAsDouble、apply、test,应用程序员甚至不需要知道,我们使用lambda表达式时就不需要写函数名。

int –> int封装为IntUnaryOperator。类似的,对于long–> long封装为LongUnaryOperator、double–> double封装为DoubleUnaryOperator。

对于参数和返回值为基本类型的一元函数,int –> long封装为IntToLongFunction、int –> double封装为IntToDoubleFunction、long–> int封装为LongToIntFunction、long–> double封装为LongToDoubleFunction、double–> int封装为DoubleToIntFunction、double–> long封装为DoubleToLongFunction。

参数为基本类型而返回值为R的一元函数,int –> R封装为IntFunction、long–> R封装为LongFunction、double–> R封装为DoubleFunction;

参数为T而返回值为int等情况,T–> int封装为ToIntFunction、T–> long封装为ToLongFunction、T–> double封装为ToDoubleFunction。

更一般的一元函数,T–> R封装为Function。如果是T–> T,则特化为UnaryOperator。

一元函数就有了

17个函数接口。下面是几个例子。

public static void unaryFunction(){//一元函数

F y = x-> x + 1;

pln(y.f(2));

IntUnaryOperator y1 = x-> x + 1; //int –> int

pln(y1.applyAsInt(2));

IntToDoubleFunction y2 = x-> x + 1; //int –> double

pln(y2.applyAsDouble(2));

ToIntFunctiony3 = s->s.length();//T–> int

pln(y3.applyAsInt("hello"));

IntFunctiony4 = i->(i+"ok");//int–> R

pln(y4.apply(5));

Functiony5 = s->s.length();//String–> Integer

pln(y5.apply("hello"));

}

从一元函数扩展到

二元函数,Java中不能够定义不同个数参数的泛型类,如DoubleOP、DoubleOP、DoubleOP,必须为它们取不同的名字,真是醉了。可以从自定义的double op(double m,double n)出发,也可以从一般到特殊:

一般的二元函数,(T, U) –> R封装为BiFunction。如果是(T, T)–> T,则特化为BinaryOperator。

(T, U) –> int封装为ToIntBiFunction、(T, U) –> long封装为ToLongBiFunction、(T, U) –> double封装为ToDoubleBiFunction。

(int, int) –> int封装为IntBinaryOperator;类似的,(long, long) –> long封装为LongBinaryOperator、(double, double) –> double封装为DoubleBinaryOperator。

(int, long) –> int这样的组合太多,不提供封装类。

本系列中25个接口。

public static void testFunction(){

principle.callback.lower.DoubleOPf = ( x, y)->{return x +y.length() ;};

double d = f.applyAsDouble(1.0,"yqj2065");

pln(d);

ToDoubleBiFunctionf1 = ( m, n)->{return m +2*n ;};

d = f1.applyAsDouble(1.0,3.0); pln(d);

Functionf2 = x->(double)x.length();

d = f2.apply("yqj2065"); pln(d);

BiFunctionf3 = (x,y)->x.length()+y.length()*1.0;

d = f3.apply("yqj","2065"); pln(d);

BinaryOperatorf4 = (x,y)->x+y;

d = f4.apply(1.0,3.0); pln(d);

DoubleBinaryOperator f5 = (x,y)->x+y;

d = f5.applyAsDouble(1.0,3.0); pln(d);

}

通用函数接口最大的用途,是框架或/和类库的设计。假设下面代码中m()是库函数,应用程序test2()可以视m()为一个高阶函数,它使用函数接口作为形参,而应用程序以lambda表达式作为实参。

public static void m(Predicatep, String t) {

if (p.test(t)) {

System.out.println("ok");

}

}

public static void test2(){

m(s->s.startsWith("a"),"aab");

}

2.Predicate

谓词/Predicate,针对T数据进行测试,返回boolean。lambda表达式的类型签名T→boolean。

上面m()的形参为Predicate,针对String数据进行测试;通常的形参为Predicate或Predicate super T>——只读通配符(T 的某个父类型)

下面的例子,说明我们可以针对List抽象出一般性的方法filter——换言之,过滤等函数不是什么了不起的想法,但在Stream中,它是延迟计算的,因而更给力。

public static Listfilter(Listlist, Predicatep) {

Listresults = new ArrayList<>();

/*list.stream().filter((s) -> (p.test(s))).//惰性

forEach((s) -> {

results.add(s);

});*/

for (T s : list) {

if (p.test(s)) {

results.add(s);

}

}

return results;

}

Predicate提供了default方法与或非and、or、 negate()。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值