通用函数接口java.util.function.*[
2015-10-20 16:13
567 查看
【2.1.4 通用函数接口java.util.function.* 返回目录】
为了配合λ表达式的使用,需要一些作为形参的函数接口。java.util.function包基本覆盖了程序员对函数接口的各种需求。
从方法的类型签名分析,java.util.function包的核心函数接口有4个。
Ø 函数型T ->R,完成参数类型T向结果类型R的转换。核心函数接口Function
Ø 判断型T ->boolean,核心函数接口Predicate/谓词
Ø 消费型T ->void,核心函数接口Consumer
Ø 供给型void->T,核心函数接口Supplier
其他函数接口与它们类似。各种类型签名可以拓展到二元如(T, U) -> R,三元及更多元的类型签名,难以支持。
由于Java泛型采用擦除技术,Java中不可以用同一个名字定义不同类型或不同数量的参数的泛型类,即无法定义Function<T, R>、Function<T,U, R>、Function<Integer, Integer >等,而必须取不同的类型名字。
另一方面,通过自定义函数接口,可以掌握通用函数接口的使用,这里就不举例说明通用函数接口的用法了。
ToIntFunction<String>f = s->s.length();
值得注意的是,在λ表达式中调用某个类的方法或构造器的情形,Java 8提供了λ表达式的简化版本,方法引用。
ToIntFunction<String>f = String::length;
方法引用是否会降低代码的可读性,见仁见智。
Consumer<String> c= 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<R>
、long–> R封装为LongFunction<R>、double–> R封装为DoubleFunction<R>;
参数为T而返回值为int等情况,T–> int封装为ToIntFunction<T>、T–> long封装为ToLongFunction<T>、T–> double封装为ToDoubleFunction<T>。
更一般的一元函数,T–> R封装为Function<T,R>。如果是T–> T,则特化为UnaryOperator<T>。
一元函数就有了17个函数接口。下面是几个例子。
从一元函数扩展到二元函数,Java中不能够定义不同个数参数的泛型类,如DoubleOP<T,U>、DoubleOP<T,U,V>、DoubleOP<T,U,V,W>,必须为它们取不同的名字,真是醉了。可以从自定义的double op(double m,double n)出发,也可以从一般到特殊:
一般的二元函数,(T, U) –> R封装为BiFunction<T,U,R>。如果是(T, T)–> T,则特化为BinaryOperator<T>。
(T, U) –> int封装为ToIntBiFunction<T,U>、(T, U) –> long封装为ToLongBiFunction<T,U>、(T, U) –> double封装为ToDoubleBiFunction<T,U>。
(int, int) –> int封装为IntBinaryOperator;类似的,(long, long) –> long封装为LongBinaryOperator、(double, double) –> double封装为DoubleBinaryOperator。
(int, long) –> int这样的组合太多,不提供封装类。
本系列中25个接口。
public static void testFunction(){
通用函数接口最大的用途,是框架或/和类库的设计。假设下面代码中m()是库函数,应用程序test2()可以视m()为一个高阶函数,它使用函数接口作为形参,而应用程序以lambda表达式作为实参。
上面m()的形参为Predicate<String>,针对String数据进行测试;通常的形参为Predicate<T>或Predicate<?
super T>——只读通配符(T 的某个父类型)
下面的例子,说明我们可以针对List<T>抽象出一般性的方法filter——换言之,过滤等函数不是什么了不起的想法,但在Stream中,它是延迟计算的,因而更给力。
为了配合λ表达式的使用,需要一些作为形参的函数接口。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<T, R>、Function<T,U, R>、Function<Integer, Integer >等,而必须取不同的类型名字。
2.避免装箱和拆箱
Java泛型仅针对引用类型,如果使用Function<Integer, Integer>,会将代码中的int进行装箱,从而在性能上付出代价。java.util.function包针对基本类型的int、double和long提供支持,当输入或/和输出为基本类型时,可以避免自动装箱的操作。核心函数接口 | 简化或二元拓展 | 基本类型 |
Function<T, R> ,T ->R 共25个接口 | | IntFunction<R>,int->R LongFunction<R> DoubleFunction<R> IntToDoubleFunction, int->double IntToLongFunction LongToDoubleFunction, LongToIntFunction, DoubleToIntFunction DoubleToLongFunction ToIntFunction<T>, T->int ToDoubleFunction<T>, ToLongFunction<T> |
BiFunction<T,U,R> ,(T,U) ->R | ToIntBiFunction<T,U>, ToLongBiFunction<T,U>, ToDoubleBiFunction<T,U> | |
UnaryOperator<T>, T->T | IntUnaryOperator, LongUnaryOperator, DoubleUnaryOperator | |
BinaryOperator<T> (T,T) ->T | IntBinaryOperator, LongBinaryOperator, DoubleBinaryOperator | |
Predicate<T> T->boolean 共5个接口 | | IntPredicate, LongPredicate, DoublePredicate |
BiPredicate<L,R> (L,R)->boolean | | |
Consumer<T>, T->void 共8个接口 | | IntConsumer, LongConsumer, DoubleConsumer |
BiConsumer<T,U> (T,U)->void | ObjIntConsumer<T>, ObjLongConsumer<T>, ObjDoubleConsumer<T> | |
Supplier<T> ()->T 共5个接口 | | BooleanSupplier, IntSupplier, LongSupplier, DoubleSupplier |
3.自定义函数接口
虽然java.util.function包提供了通用函数接口,在后面的例子中仍然使用了自定义函数接口,主要考虑在演示代码中自定义函数接口比通用函数接口更容易理解。某种程度上,使用通用函数接口,如同定义各种类时,将各种类的方法全部命名为doSth()一样,会降低代码的可读性。另一方面,通过自定义函数接口,可以掌握通用函数接口的使用,这里就不举例说明通用函数接口的用法了。
4.方法引用
函数接口主要支持λ表达式的使用,例如ToIntFunction<String>f = s->s.length();
值得注意的是,在λ表达式中调用某个类的方法或构造器的情形,Java 8提供了λ表达式的简化版本,方法引用。
ToIntFunction<String>f = String::length;
方法引用是否会降低代码的可读性,见仁见智。
Consumer<String> c= 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<R>
、long–> R封装为LongFunction<R>、double–> R封装为DoubleFunction<R>;
参数为T而返回值为int等情况,T–> int封装为ToIntFunction<T>、T–> long封装为ToLongFunction<T>、T–> double封装为ToDoubleFunction<T>。
更一般的一元函数,T–> R封装为Function<T,R>。如果是T–> T,则特化为UnaryOperator<T>。
一元函数就有了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)); ToIntFunction<String> y3 = s->s.length();//T–> int pln(y3.applyAsInt("hello")); IntFunction<String> y4 = i->(i+"ok");//int–> R pln(y4.apply(5)); Function<String,Integer> y5 = s->s.length();//String–> Integer pln(y5.apply("hello")); }
从一元函数扩展到二元函数,Java中不能够定义不同个数参数的泛型类,如DoubleOP<T,U>、DoubleOP<T,U,V>、DoubleOP<T,U,V,W>,必须为它们取不同的名字,真是醉了。可以从自定义的double op(double m,double n)出发,也可以从一般到特殊:
一般的二元函数,(T, U) –> R封装为BiFunction<T,U,R>。如果是(T, T)–> T,则特化为BinaryOperator<T>。
(T, U) –> int封装为ToIntBiFunction<T,U>、(T, U) –> long封装为ToLongBiFunction<T,U>、(T, U) –> double封装为ToDoubleBiFunction<T,U>。
(int, int) –> int封装为IntBinaryOperator;类似的,(long, long) –> long封装为LongBinaryOperator、(double, double) –> double封装为DoubleBinaryOperator。
(int, long) –> int这样的组合太多,不提供封装类。
本系列中25个接口。
public static void testFunction(){
principle.callback.lower.DoubleOP<Double,String> f = ( x, y)->{return x +y.length() ;}; double d = f.applyAsDouble(1.0,"yqj2065"); pln(d); ToDoubleBiFunction<Double,Double> f1 = ( m, n)->{return m +2*n ;}; d = f1.applyAsDouble(1.0,3.0); pln(d); Function<String,Double> f2 = x->(double)x.length(); d = f2.apply("yqj2065"); pln(d); BiFunction<String,String,Double> f3 = (x,y)->x.length()+y.length()*1.0; d = f3.apply("yqj","2065"); pln(d); BinaryOperator<Double> f4 = (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(Predicate<String> p, String t) { if (p.test(t)) { System.out.println("ok"); } } public static void test2(){ m(s->s.startsWith("a"),"aab"); }
2.Predicate<T>
谓词/Predicate,针对T数据进行测试,返回boolean。lambda表达式的类型签名T→boolean。上面m()的形参为Predicate<String>,针对String数据进行测试;通常的形参为Predicate<T>或Predicate<?
super T>——只读通配符(T 的某个父类型)
下面的例子,说明我们可以针对List<T>抽象出一般性的方法filter——换言之,过滤等函数不是什么了不起的想法,但在Stream中,它是延迟计算的,因而更给力。
public static <T> List<T> filter(List<T> list, Predicate<T> p) { List<T> results = 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实现视频截帧的方法
- Hbase-1.1.2 Java API 操作
- [Java]剑走偏锋的10条编程技巧
- java.util.concurrent.ExecutionException
- java历史集合类对比
- Java中遍历数组的三种方式复习
- Java学习笔记——关于java中类中乱七八糟的概念的梳理
- Java核心技术第12章(1)
- JAVA 内部静态类--解析静态内部类的使用目的与限制
- Java程序初始化顺序
- struts2数据校验方法
- 解决java compiler level does not match the version of the installed java project facet
- spring中注解注入的实现(转)
- Java Base64加密解码实例
- 静态资源[org.springframework.web.servlet.PageNotFound]
- javaMail使用imap协议接收邮件
- JavaWriter源码分析
- JSON数据交互(SpringMVC进行json交互)
- mac 系统上使用eclipse 测试找不到真机设备
- java判断时间大小