赞
踩
概念
函数式接口在Java中是指:有且仅有一个抽象方法的接口。函数式接口,既适用于函数式编程场景的接口。而Java中的函数式编程体现就是Lambda,所以函数式接口就是可以适用于Lambda使用的接口。只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导。
备注:“语法糖” 是指使用更加方便,但是原理湖边的代码语法。例如在遍历结合时使用发for-each语法,其实底层的实现原理仍然是迭代器,这便是"语法糖"。从应用层面来讲,Java中的Lambda可以被当做是匿名内部类的"语法糖",但是二者在原理上是不同的。
格式
只要确保接口中有且仅有一个抽象方法即可:
修饰符interface接口名称{
public adstract 返回值类型 方法名称(可选参数信息);
}
@FunctionalInterface注解
与 @Override 注解的作用类似,Java 8中专门为函数式接口引入了一个新的注解: @FunctionalInterface 。该注解可用于一个接口的定义上:
/**
只有一个抽象方法的接口就是函数式接口
*/
@FunctionalInterface //声明接口必须是一个函数式接口
public interface MyInterface01 {
void show();
}
代码使用:
public class Demo01 { public static void main(String[] args) { //函数式接口,主要是在函数式编程(lambda)中使用 //非函数式编程 method01(new MyInterface01() { @Override public void show() { System.out.println("hello functional interface 111"); } }); //函数式编程:就是在匿名内部类对象的基础上,省略接口名及方法名.是语法糖 method01(() -> { System.out.println("hello functional interface 111"); }); //简写方式:如果方法体中只有一行代码,可以省略方法体的大括号 method01(()-> System.out.println("hello functional interface 111")); } public static void method01(MyInterface01 inter01){ inter01.show(); } }
函数式接口的优势
函数式接口比匿名内部类对象产生更少的字节码对象,提升java执行效率
函数式接口作为参数,方法带参数
接口:
@FunctionalInterface
public interface MyInterface02 {
void show(String msg , int num);
}
代码使用:
public class Demo02 { public static void main(String[] args) { //非函数式 method(new MyInterface02() { @Override public void show(String msg, int num) { System.out.println(msg + " , " + num); } }); //函数式 method((String msg, int num) -> { System.out.println(msg + " , " + num); } ); //简写方式一:省略了形参的类型 method((msg, num) -> { System.out.println(msg + " , " + num); } ); //简写方式二: 省略方法体的大括号 method((msg, num) -> System.out.println(msg + " , " + num)); } public static void method(MyInterface02 inter) { inter.show("hello", "你好"); }
}
函数式接口作为返回值,方法带参数
//定义函数式接口 public interface MyInterface02 { public abstract void show(String msg1, String msg2); } public static void main(String[] args) { getInter1().show("你好", "函数式"); getInter2().show("你好", "函数式"); } // 函数式完整 public static MyInterface02 getInter1() { return (String msg1, String msg2) ‐> { System.out.println(msg1 + msg2); }; } // 函数式简写 public static MyInterface02 getInter2() { return (msg1, msg2) ‐> System.out.println(msg1 + msg2); }
函数式接口的方法有返回值
//定义函数式接口 public interface MyInterface03 { public abstract String getMsg(); } public static void main(String[] args) { showMsg(new MyInterface03() { @Override public String getMsg() { return "hello functional interface"; } }); // lambada表达式 showMsg(() ‐> { return "hello1 functional interface"; }); // lambda表达式简写 showMsg(() ‐> "hello1 functional interface"); } public static void showMsg(MyInterface03 inter) { String msg = inter.getMsg(); System.out.println(msg); }
概念
在兼顾面向对象特性的基础上,Java语言通过Lambda表达式使用函数式接口,就叫做函数式编程
使用Lambda作为参数
如果抛开实现原理不说,Java中的Lambda表达式可以被当做是i匿名内部类的替代品。如果方法的参数是一个函数式接口类型,那么就可以使用Lambda表达式进行替代
public class Demo07 { public static void main(String[] args) { startThread(new Runnable() { @Override public void run() { System.out.println("线程要做的事"); } }); startThread(() -> { System.out.println("线程要做的事"); } ); //比第一种的可阅读性强很多!!! startThread(() -> System.out.println("线程要做的事~~~")); } public static void startThread(Runnable runnable) { new Thread(runnable).start(); } }
使用函数式接口作为返回值
如果一个方法的返回值类型是一个函数式接口,那么就可以直接返回一个Lambda表达式
public class Demo08 { public static void main(String[] args) { Integer[] nums = {1 ,4 ,8,3}; Arrays.sort(nums,getComparator3()); for (Integer num : nums) { System.out.println(num); } } public static Comparator<Integer> getComparator1(){ return new Comparator<Integer>() { @Override public int compare(Integer num1, Integer num2) { return num1 - num2; } }; } public static Comparator<Integer> getComparator2(){ return (num1 , num2) -> { return num1 - num2; }; } public static Comparator<Integer> getComparator3(){ //如果方法体中,只有一行代码,且该代码是一个返回语句,就可以省略大括号及return关键字 return (num1 , num2) -> num1 - num2; } }
传统日志
public class Demo09 { public static void main(String[] args) { String msg1 = "日志1"; String msg2 = "日志2"; String msg3 = "日志3"; printLog(2, msg1 + msg2 + msg3); } public static void printLog(int level , String msg){ if (level == 1) { System.out.println(msg); } } }
这段代码存在问题:无论级别是否满足 要求,作为log方法的第二个参数,三个字符串一定会首先被拼接并传入方法内,然后才会进行级别判断。如果级别不符合要求,那么字符串的拼接操作就白做了,存在性能浪费
函数式日志
/**
* 提供日志内容
*/
@FunctionalInterface
public interface MyInterface06 {
String getMsg();
}
代码实现:
public class Demo09 { public static void main(String[] args) { //level=1 , 拼接msg1,msg2,msg3. 应该打印日志 //level=2 , 拼接msg1,msg2,msg3. 不应该打印日志 String msg1 = "日志1"; String msg2 = "日志2"; String msg3 = "日志3"; printLog(1, new MyInterface06() { @Override public String getMsg() { return msg1 + msg2 + msg3; } }); printLog(1, () -> { return msg1 + msg2 + msg3; } ); printLog(1 ,() -> msg1 + msg2 + msg3); } public static void printLog(int level , MyInterface06 inter){ if (level == 1) { //只有当日志级别为1的时候,采取获取日志,并打印日志 System.out.println(inter.getMsg()); } } }
JDK提供了大量常用的函数式接口以丰富Lambda的典型使用场景,它们主要在java.util.function包中被提供。下面是简单的几个接口及使用示例
Supplier接口
java.util.function.Supplier接口仅包含一个无参的方法:T get()。 用来获取一个泛型参数指定类型的对象数据。由于这是一个函数式接口,这也就意味着对应的Lambda表达式需要“对外提供”一个符合泛型类型的对象数据
基本使用
public static void main(String[] args) { //非函数式 String msg1 = getMsg(new Supplier<String>() { @Override public String get() { return "abc"; } }); System.out.println(msg1); String msg2 = getMsg(() -> { return "abc"; }); System.out.println(msg2); String msg3 = getMsg(() -> "abc"); System.out.println(msg3); } /** * getMsg方法的返回值是由Supplier接口中的get方法返回!!! * * @param supplier * @return */ public static String getMsg(Supplier<String> supplier) { return supplier.get(); }
综合案例
需求:
使用Supplier接口作为方法参数类型,通过Lambda表达式求出int数组中最大值。
提示:接口的泛型请使用java.lang.integer类。
public class Demo11 { public static void main(String[] args) { // 使用 Supplier 接口作为方法参数类型,通过Lambda表达式求出int数组中的最大值。 Integer max1 = getMax(new Supplier<Integer>() { @Override public Integer get() { int[] nums = {1, 2, 3, 4, 5}; int max = nums[0]; for (int num : nums) { if (max < num) { max = num; } } return max; } }); System.out.println(max1); System.out.println("-----------"); Integer max2 = getMax(() -> { int[] nums = {1, 2, 3, 4, 5}; int max = nums[0]; for (int num : nums) { if (max < num) { max = num; } } return max; }); System.out.println(max2); } /** * 获取最大值 * @param supplier * @return */ public static Integer getMax(Supplier<Integer> supplier){ return supplier.get(); } }
Consumer接口java.util.function.Consumer 接口则正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据,其他数据类型由泛型决定
accept方法Consumer接口中包含抽象方法void accept(T t),意为消费一个指定泛型的数据。基本使用如:
public class Demo12 { public static void main(String[] args) { print(new Consumer<String>() { @Override public void accept(String s) { System.out.println(s); } },"helloworld1"); print(( s)-> { System.out.println(s); },"helloworld2"); print((msg) -> System.out.println(msg) ,"helloworld3"); } //定义一个变量,由Consumer来消费该变量,就打印 public static void print(Consumer<String> consumer , String msg) { consumer.accept(msg); } }
andThen方法
如果 一个方法的参数和返回值全都是Consumer类型,那么就可以实现效果:消费数据时候,首先做一个操作,然后再做一个操作,实现组合。而这个方法就是Consumer接口中的default方法andThen
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
注:java.uitl.Objects的requireNonNull静态方法将会在参数null时主动抛出NullPointerException异常。这省去了重复编写if语句和抛出空指针异常的麻烦
public class Demo13 { //递归:同一个对象的方法调用自己 public static void main(String[] args) { consumeMsg("武汉加油", new Consumer<String>() { @Override public void accept(String s) { System.out.println(s); } }, new Consumer<String>() { @Override public void accept(String s) { System.out.println(s+"中国加油"); } }); consumeMsg("武汉加油", (String s) -> { System.out.println(s); }, (String s) -> { System.out.println(s+"中国加油"); }); consumeMsg("武汉加油!",msg-> System.out.println(msg+"a"),msg-> System.out.println(msg+"中国加油!")); } public static void consumeMsg(String msg , Consumer<String> consumer1 , Consumer<String> consumer2){ consumer1.andThen(consumer2).accept(msg); } }
需求
下面的字符串数组当中存有多条信息,请按照格式“姓名:xx。性别:xx”的格式讲信息打印出来。要求将打印姓名的动作作为第一个Consumer接口的Lambda实例,将打印性别动作作为第二个Consumer接口的Lambda实例,将两个Consumer接口按照顺序“拼接”到一起
String[] arrays = {"迪丽热巴,女", "老邱,男", "古力娜扎扎,女"};
实现
public class Demo14 { public static void main(String[] args) { String[] arrays = {"迪丽热巴,女", "鑫磊,男", "古力娜扎扎,女"}; printInfo(arrays, new Consumer<String>() { @Override public void accept(String arr) { System.out.print("姓名:" + arr.split(",")[0] + "."); } }, new Consumer<String>() { @Override public void accept(String arr) { System.out.println("性别:" + arr.split(",")[1]); } } ); printInfo(arrays, (arr) -> { System.out.print("姓名:" + arr.split(",")[0] + "."); }, (arr) -> { System.out.println("性别:" + arr.split(",")[1]); } ); printInfo(arrays, arr -> System.out.print("姓名:" + arr.split(",")[0] + "."), arr -> System.out.println("性别:" + arr.split(",")[1]) ); } public static void printInfo(String[] arrays, Consumer<String> consumer1, Consumer<String> consumer2) { for (String arr : arrays) { //consumer1和consumer2都在消费array数组中的每一个元素 consumer1.andThen(consumer2).accept(arr); } } }
有时候我们需要对某种类型数据进行判断,从而得到一个Boolean值结果,这时候可以使用java.util.function.Predicate接口
test方法
Predicate接口中包含一个抽象方法:boolean(T t)。用户条件判断的场景
public class Demo15 { // 判断字符是否等于4 public static void main(String[] args) { boolean isLength1 = isLength("hello", new Predicate<String>() { @Override public boolean test(String msg) { return 4 == msg.length(); } }); System.out.println(isLength1); boolean isLength2 = isLength("hello", (String msg) -> { return msg.length() == 4; }); System.out.println(isLength2); boolean isLength3 = isLength("hello", msg -> msg.length() == 4); System.out.println(isLength3); } public static boolean isLength(String msg,Predicate<String> predicate ){ return predicate.test(msg); } }
综合案例
需求:
将姓名长度为4,性别为女 的信息保存并打印
public class Demo16 { public static void main(String[] args) { String[] arrays = {"迪丽热巴,女", "鑫磊,男", "古力娜扎扎,女"}; // List<String> list = method(arrays, arr -> arr.split(",")[0].length() == 4 && arr.split(",")[1].equals("女")); // System.out.println(list); List<String> list1 = method1( arrays, arr -> arr.split(",")[0].length() == 4, arr -> arr.split(",")[1].equals("女") ); System.out.println(list1); } public static List<String> method(String[] arrays,Predicate<String> predicate){ List<String> list = new ArrayList<>(); for (String array : arrays) { boolean test = predicate.test(array); if (test) { list.add(array); } } return list; } public static List<String> method1(String[] arrays,Predicate<String> predicate1 ,Predicate<String> predicate2){ List<String> list = new ArrayList<>(); for (String array : arrays) { boolean test = predicate1.negate().and(predicate2).test(array); if (test) { list.add(array); } } return list; } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。