概述
【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()。
最后
以上就是忧心芹菜为你收集整理的java通用函数_通用函数接口java.util.function.*[的全部内容,希望文章能够帮你解决java通用函数_通用函数接口java.util.function.*[所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复