900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > Java函数式接口--抽象方法接口

Java函数式接口--抽象方法接口

时间:2022-02-11 06:16:41

相关推荐

Java函数式接口--抽象方法接口

1 函数式接口

函数式接口在Java中是指:有且仅有一个抽象方法的接口

函数式接口, 即适用于函数式编程场景的接口; 而Java中函数式编程体现就是Lambda, 所以函数式接口就是可以适用于Lambda使用的接口; 只有确保接口中有且仅有一个抽象方法, Java中的Lambda才能顺利地进行推导

备注 : “语法糖"是指使用更加方便, 但是远离不变的代码语法; 例如在遍历集合时使用的for-earch语法, 其实底层实现的仍然是迭代器, 这便是"语法糖”, 从应用层面来讲, Java中的Lambda可以被当做匿名内部类的"语法糖", 但是二者在原理上是不同的

格式 : 只要确保接口中有且仅有一个抽象方法即可

修饰符 interface 接口名称{public abstract 返回值类型 方法名称(可选参数信息);// 其他非抽象方法内容}

由于接口中抽象发的public abstract是可以省略的, 所以定义一个函数式接口很简单

public interface MyFunctionInterface{void myMethod();}

@FunctionalInterface注解

@Override注解的作用类似, Java8中专门为函数式接口引入了一个新的注解 :@FunctionalInterface, 该注解可用于一个接口的定义上

函数式接口的使用

/* 函数式接口的使用 : 一般作为方法的参数与返回值类型 */public class Demo{// 定义一个方法, 参数使用函数式接口MyFunctionalInterfacepublic static void show(MyFunctionalInterface myInter){myInter.method()}public static void main(String[] args){// 调用show方法, 方法的参数是一个接口,所以可以传递接口的实现类对象show(new MyFunctionInterfaceImpl);// 调用show方法, 方法的参数是一个接口, 所以我们可以传递接口的匿名内部类show(new MyFunctionInterface{@Overridepublic void method(){System.out.println("使用匿名内部类重写接口中的抽象方法")}});}// 调用方法, 方法的参数是一个函数式接口, 所以可以使用Lambda表达式show(() -> {System.out.println("使用Lambda表达式重写接口中的抽象方法")});// Lambda简化show( -> System.out.println("使用Lambda表达式重写接口中的抽象方法"));}

2 函数式编程

Lambda的延迟执行

有些场景的代码执行后, 结果不一定会被使用, 从而造成性能浪费, 而Lambda表达式是延迟执行的, 这正好可以作为解决方案, 提升性能

性能浪费的日志案例

日志可以帮助我们快速的定位问题, 记录程序运行过程中的情况, 以便项目的监控和优化

一种典型的场景就是对参数进行有条件使用, 例如对日志消息进行拼接后 ,在满足条件的情况下进行打印输出

public class Demo01Logger{private static void log(int level,String msg){// 根据日志级别,显示日志信息的方法if(level == 1){System.out.println(msg);}}public static void main(String[] args){String msgA = "Hello";String msgB = "World";String msgC = "Java";log(1,msgA+msgB+msgC);}}

以上的代码问题在于, 当传入的level 不为1时, 还是海鲜拼接字符,造成性能上的浪费

我们要做的是 当第一个参数不满足的shih,不执行后面的操作; 避免性能的浪费

// 接口类@FunctionalInterfacepublic interface MessageBuilder{// 定义一个拼接消息的抽象方法, 返回被拼接的信息public abstract String builderMessaeg();}public class Demo02Lambda{// 定义一个现实日志的方法, 方法的参数传递日志的等级和MessageBuilder接口public static vid showLog(int level,MessageBuilder mb){// 对日志的等级记性判断,如果是1级,则调用接口中的方法if(level==1){System.out.println(mb.builderMessage());}}public static void main(String[] args){String msgA = "Hello";String msgB = "World";String msgC = "Java";// 调用showLog方法,参数MessageBuilder是一个函数式接口,所以可以使用Lambda表达式shoLog(1, () ->{return msgA+msgB+msgC;});}// lambda表达式作为参数传递, 仅仅是把参数传递到showLog方法中, 当level=1,才会调用接口中的方法如果条件不满足,则接口中的方法不会执行, 不存在性能浪费}

使用Lambda作为参数和返回值

在使用比较器接口的时候, 可以使用Lambda表达式, 避免重写compare方法

3 常用函数式接口

Supplier接口

java.util.function.Supplier<T>接口仅包含一个无参的方法 : T get(); 用来获取一个泛型指定类型的对象数据; 由于这是一个函数式接口, 这也意味着对应的Lambda需要对外提供一个符合泛型类型的对象数据

Supplier<T> 接口被称之为生产型接口, 指定接口的泛型是什么类型, 那么接口中的get方法就会返回什么类型

import java.function.Supplierpublic class Demo01Supplier{private Static getString(Supplier<String> sup){return sup.get();}public static void main(String[] args){String msgA = "Hello";String msgB = "World";System.out.println(getString(() -> msgA+msgB)));}}

练习 : 求数组中元素最大值

使用supplier接口作为方法参数类型, 通过Lambda表达式求出int数组中的最大值, 提示 : 接口的泛型请使用java.lang.Integer

public class Demo02Test{public static int getMax(Supplier<Integer> sup){return sup.get();}public static vid main(String[] args){int arr[] = {2,56,48,3,9,64};// 调用getMax方法,参数传递Lambdaint manNum = getMax(() -> {// 计算数组中的最大值int max = arr[0];// 遍历数组for(int i:arr){if(i>max){max = i;}}return max;});System.out.println("数组中最大的元素"+max);}}

Consumer 接口

java.util.function.Consumer<T>接口则正好与Supplier相反, 它不是生产一个数据, 而是消费一个数据, 其数据类型由类型决定

抽象方法 :accept

Consumer接口中包含抽象方法void accept(T t), 意为消费一个指定泛型的数据,基本使用

import javautil.function.Consumerpublic class Demo01Consumer {private static void consumeString(String name,Consumer<String> con){con.accept(name);}public static int method(Supplier<Integer> sup){consumeString(s -> System.out.println(s));}}

更好地方法是使用 方法引用

默认方法 : andThen()

如果一个方法的参数和返回值都是Consumer类型., 那么就可以实现效果 : 消费数据的时候, 实现先做一个操作, 然后在做一个操作, 实现组合; 而这个方法就是接口中的默认方法 andThen, 源码是

default Consumer<T> andThen(Consumer<? super T> after){Objects.requireNonNull(after);return (T t) -> {accept(t);after.accept(t);};}

备注 :java.util.ObjectsrequireNonNull静态方法将会在参数为null时 主动抛出NullPointerException异常, 这省去了重复编写if语句和抛出空指针异常的麻烦

要想实现组合, 需要两个或多个Lambda表达式即可, 而andThen的语义正式"一步接一步"操作, 例如两个步骤组合的情况:

public class Demo02AndThen{public static void method(String s,Consumer<String> con1,Consumer<String> con2){// 连接两个consumer,再消费con1.andThen(con2).accept(s);}public static void main(String[] args){meth("hello",(t) ->{System.out.println(t.toUpperCase());}, (t) ->{System.out.println(t.toLowerCase());})}}

格式化打印信息

public class Demo03Test{public static void printInfo(String[] arr,Consumer<String> con1,Consumer<String> con2){// 遍历字符串数组for(String message: arr){con1.andThen(con2).accept(messge);}}public static void main(String[] args){// 定义一个字符串类型的数组String[] arr = {"迪丽热巴,女","古力娜扎,女","马尔扎哈,男"};printInfo(arr,(message)->{String name = meaasge.split(",")[0];System.out.print("姓名: "+name);},(meaasge)->{String age = messaeg.aplit(",")[1];System.out.println("年龄 :"+age);})}}

Predicate接口

有时我们需要对某种类型数据进行判断, 从而得到一个boolean值结果, 这时就可以使用jav.util.function.Predicate<T>接口

抽象方法 : test

Predicate接口中包含一个抽象方法 :boolean test\<T t>, 用于条件判断场景

public class DemoPredicateTest{public static void method(Predicate<String> preficate){boolean veryLong = predicate.test("HelloWorld");System.out.println("字符串很长吗 :"+veryLong);}public static void main(String[] args){method(s -> s.length() >5);}}

条件判断的标准是传入的Lambda表达式逻辑, 只要字符串长度大于5 则认为很长

默认方法 : and

既然是条件判断, 就会存在与, 或, 非三种常见的逻辑关系; 其中将两个predicate条件使用"与"逻辑连接起来实现"并且"的效果时, 就可以使用default方法and,JDK源码为

default Predicate<T> and(Predicate<? super T> other){Objects.requireNonNull(other);return (t) ->test(t) && other.test(t);}

如果要判断一个字符既要包含大写"H", 又要包含大写"W", 那么:

public class DemoPredicateAnd{public static void method(Predicate<String> one,Predicate<String> two){boolean isValid = one.and(two).test("HelloWorld");System.out.println("字符串很长吗 :"+isVaild);public static void main(String[] args){method(s -> s.contains("H"),s -> s.contains("W"));}}}

默认方法 : or

与 and 的与类似 , 默认方法 or 实现逻辑关系中的 或 , JDK源码为

default Predicate<T> or(Predicate<? super T> other){Objects.requireNonNull(other);return (t) ->test(t) || other.test(t);}

如果实现逻辑字符串包含大写 H 或者大写 W , 那么大代码中西药将 and 修改为 or即可, 其他不变

public class DemoPredicateOr{public static void method(Predicate<String> one,Predicate<String> two){boolean isValid = one.or(two).test("HelloWorld");System.out.println("字符串很长吗 :"+isVaild);public static void main(String[] args){method(s -> s.contains("H"),s -> s.contains("W"));}}

默认方法 : negate

与 或已经了解了, 剩下的 非(取反)也非常简单, 默认发 negate的 JDK源码为

default Predicate<T> negate(){return (t) -> !test(t);}

从实现中很容易看出, 它执行了test方法之后, 对结果boolean值进行 ! 取反而已; 一定要在 test 方法调用之前调用 negate 方法, 正如and 和 or方法一样

public class DemoPredicateNegate{public static boolean checkString(String s,Predicate<String> pre){return !pre.test(s);}public static void main(String[] args){String = "abc"boolean b = checkString(s,(String str)-> {return str.length > 5});}}

练习 : 集合信息筛选

数组当中有多条 “姓名+性别” 的信息如下, 请通过Pridicate 接口的拼接将符合要求的字符串筛选到集合ArrayList 中, 需要同时满足下面两个条件:

必须为女生姓名为4个字

public class DemoPredicate{public static void main(String[] args){String[] arra = {"迪丽热巴,女","古力娜扎,女","马尔扎哈,男","赵丽颖,女"}} }

public class DemoTest{public class ArrayList<String> filter(String[] arr,Predicate<String> pre1,Predicate<String> pre1){ArrayList<String> list = new ArrayList<>();for(String a:arr){// 使用接口之间中的test方法对获取的字符进行判断boolean b = pre1.and(pre2).test(s);if(b){list.add(s)}}return list}public static void main(String[] args){String[] array = {"迪丽热巴,女","古力娜扎,女","马尔扎哈,男","赵丽颖,女"}// 调用filterffafilter(array,(String s) ->{s.split(",")[1].equals("女");},(String s) ->{s.split(",")[0].length == 4;});// 遍历集合for(String s:list){System.out.println(s);}} }

Function接口

java.util.function.Function<T,R>接口用来根据一个类型的数据得到另一个数据的数据, 前者称为前置条件, 后者称为后置条件

抽象方法 : apply

Function接口中最主要的抽象方法为 :R apply(T t),根据类型的T的参数获取类型R的结果

使用的场景 : 例如将 String 类型转换为 Integer 类型

public class DemoFunctionApply{private static void change(Function<String,Integer> function){int num = function.apply(s);System.out.println(num);}public static void main(String[] args){String s = "1234";change(s,(String str) -> return Integer.parseInt(s));}}

默认方法 : andThen

andThen方法用来进行组合操作, JDK源码如下

default <V> Function<T,V> andThen(Function<? super R,? extends V>after){Objects.requireNonNull(after);return (T t) -> after.apply(apply(t));}

该方法 同样用于"先做什么. 再做什么"的场景, 和 Consumer中的andThen差不多:

public class DemoFunctionAndThen{public static void change(String s,Function<String,Integer> fun1,Function<String,Integer> fun2){String ss = fun1.andThen(fun2).apply(s);System.out.println(ss);}public static void main(String[] args){String = "123";change(s,(String str) ->{return Integer.parseInt(str)+10},(Integer i) ->{return i+"";});}}

练习: 自定义函数类型拼接

请使用Function进行函数模型的拼接, 按照顺序需要执行的多个函数操作为

​ String str = “赵丽颖,20”;

将字符串截取数字年龄部分, 得到字符串;将上一步的字符串转换为int类型的数字;将上一步的int数字累加100, 得到结果 int 数字;

public class Demo03Test{public static int change(String s,Function<String,String> fun1,Function<String,String> fun2,Function<String,String> fun3){// 使用andThen方法将三个组合到一起return fun1.andThen(fun2).andThen(fun3).apply(s);}public static void main(String[] args){String str = "赵丽颖,20";int num =change(str,(String s)->{return s.split(",")[1];},(String s)->{return Integer.parseInt(s);},(Integer i)->{return i+100});System.out.println(num);}}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。