函数式接口
- JDK8更新了很多的新特性,其中函数式接口就是JDK8中最为受欢迎的新特性之一。
什么是函数式接口?
- 有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。
函数式接口是适用于函数式编程的接口,而JDK8中的函数式编程就是Lambda,所以函数式接口就是可以使用于Lambda表达式使用的接口,只有确保接口中只有一个抽象方法,Java中的Lambda才可以顺利进行推导。
格式
1 2 3
| public interface MyFunctionInterface { void method(); }
|
@FunctionalInterface注解
@FunctionalInterface注解是一个和函数式接口相关的注解,可以检测一个接口是否是一个函数式接口。如果是则编译成功,如果不是则编译失败。
编译失败的可能原因:接口中没有抽象方法或者有多个抽象方法。
编译成功的情况:
1 2 3 4
| @FunctionalInterface public interface MyFunctionInterface { void method(); }
|
编译失败的情况:
1 2 3 4 5
| @FunctionalInterface public interface MyFunctionInterface { void method(); void method2(); }
|
Lambda表达式
Lambda表达式的重要特征:
- 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
- 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
- 可选的大括号:如果主体只包含了一行代码,可以不需要使用大括号。
- 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
Lambda表达式的使用
我们平常面向接口编程的时候,都需要实现该接口,重写内部方法,我们可以使用匿名内部类的方式编写代码,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12
| public static void show(MyFunctionInterface myInterface) { myInterface.method(); }
public static void main(String[] args) { show(new MyFunctionInterface() { @Override public void method() { System.out.println("使用匿名内部类的方式重写接口中的方法。"); } }); }
|
而如果这个接口是一个函数式接口,那么我们可以使用Lambda表达式来重写这个接口中的方法,简化代码。
1 2 3 4 5 6 7 8 9
| public static void show(MyFunctionInterface myInterface) { myInterface.method(); }
public static void main(String[] args) { show(()->{ System.out.println("使用Lambda表达式的方式重写接口中的方法。"); }); }
|
Lambda表达式的简化:如果这个重写的方法中有且仅有一行代码,那么我们可以省略方法体中的大括号和分号。
1 2 3 4 5 6 7
| public static void show(MyFunctionInterface myInterface) { myInterface.method(); }
public static void main(String[] args) { show(()->System.out.println("使用Lambda表达式的方式重写接口中的方法。")); }
|
匿名内部类和Lambda表达式的区别?
匿名内部类会生成一个匿名内部类的class文件,而Lambda表达式不会有class文件,节省了空间。
Lambda表达式的延迟执行
有些场景的代码执行后,结果有不一定会被使用,从而造成了性能的浪费,而Lambda表达式是延迟执行的,这正好可以作为解决方案,提升性能。
性能浪费的日志案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class Logger { public static void showLog(int level, String message) { if(level == 1) { System.out.println(message); } } public static void main(String[] args) { String msg1 = "Hello"; String msg2 = "World"; String msg3 = "Java"; showLog(1, msg1+msg2+msg3); } }
|
以上代码中,如果传入的level参数为2,那么message参数传入的拼接字符串就造成了性能浪费。
使用Lambda表达式的延迟执行优化这段代码
1 2 3 4
| @FunctionalInterface public interface MessageBuilder { String builderMessage(); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public static void showLog(int level, MessageBuilder mb) { if(level == 1) { System.out.println(mb.builderMessage()); } }
public static void main(String[] args) { String msg1 = "Hello"; String msg2 = "World"; String msg3 = "Java"; showLog(1, ()->{ System.out.println("当条件符合时才会执行这段方法!"); return msg1+msg2+msg3; }); }
|
使用Lambda表达式作为参数传递,仅仅是把参数传递到showLog方法中。
- 只有满足条件,参数level为1时,才会调用的MessageBuilder接口中的builderMessage方法,字符串才会进行拼接。
- 如果不满足条件,参数level不为1时,不会调用的MessageBuilder接口中的builderMessage方法,字符串也不会进行拼接。
这就是Lambda表达式的延迟执行,避免了性能的浪费。
Lambda表达式和线程
线程中的Runnable接口就是一个函数式接口,其中有且仅有一个抽象方法run()方法。
我们就可以使用Lambda表达式来创建线程。
1 2 3 4 5
| public static void main(String[] args) { new Thread(()-> System.out.println(Thread.currentThread().getName()+"线程启动了!") ).start(); }
|
常用的函数式接口
- JDK为我们提供了一些常用的函数式接口,这些接口全部放在java.util.function包中。
Supplier接口
Supplier是一个函数式接口,其中仅有一个无参的方法:T get()。用来获取一个泛型参数指定类型的对象数据。
- Supplier接口被称之为生产型接口,指定接口是什么泛型,就会生产什么类型的数据。
1 2 3 4 5 6 7 8 9 10
| public class SupplierDemo { public static String getString(Supplier<String> supplier) { return supplier.get(); } public static void main(String[] args) { String str = getString(()->"Supplier接口生产一个String类型的数据。"); System.out.println(str); } }
|
Consumer接口
- Consumer接口被称之为消费型接口,指定什么类型的泛型,就会消费什么类型的数据。
抽象方法:void accept(T t)
1 2 3 4 5 6 7 8 9 10 11 12
| public class ConsumerDemo { public static void method(String name, Consumer<String> consumer) { consumer.accept(name); } public static void main(String[] args) { method("消费了这个字符串", (t)->{ System.out.println(new StringBuffer(t).reverse()); }); } }
|
默认方法:default Consumer andThen(Consumer<? super T> after)
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class ConsumerDemo { public static void method(String name, Consumer<String> consumer1, Consumer<String> consumer2) { consumer1.andThen(consumer2).accept(name); } public static void main(String[] args) { method("消费了这个字符串", (t)->{ System.out.println("第一次消费:" + new StringBuffer(t).reverse()); },(t)->{ System.out.println("第二次消费:" + new StringBuffer(t).reverse()); }); } }
|
Predicate接口
- Predicate接口的作用是对数据类型的数据进行判断,结果返回一个boolean值。
抽象方法:test(T t)
1 2 3 4 5 6 7 8 9 10
| public class PredicateDemo { public static boolean checkString(String str, Predicate<String> predicate) { return predicate.test(str); } public static void main(String[] args) { boolean b = checkString("abcdefg", (s)->s.length() > 5); System.out.println(b); } }
|
默认方法:default Predicate and(Predicate<? super T> other)
1 2 3 4 5 6 7 8 9 10
| public class PredicateDemo { public static boolean checkString(String str, Predicate<String> predicate1, Predicate<String> predicate2) { return predicate1.and(predicate2).test(str); } public static void main(String[] args) { boolean b = checkString("abcdefg", (s)->s.length() > 5,(s)->s.contains("a")); System.out.println(b); } }
|
默认方法:default Predicate or(Predicate<? super T> other)
1 2 3 4 5 6 7 8 9 10
| public class PredicateDemo { public static boolean checkString(String str, Predicate<String> predicate1, Predicate<String> predicate2) { return predicate1.or(predicate2).test(str); } public static void main(String[] args) { boolean b = checkString("abcdefg", (s)->s.length() > 5,(s)->s.contains("h")); System.out.println(b); } }
|
默认方法:default Predicate negate()
- 相当于逻辑表达式!(非)
1 2 3 4 5 6 7 8 9 10
| public class PredicateDemo { public static boolean checkString(String str, Predicate<String> predicate) { return predicate.negate().test(str); } public static void main(String[] args) { boolean b = checkString("abcdefg", (s)->s.length() > 5); System.out.println(b); } }
|
Function接口
- Function接口用来根据一个类型的数据得到另一个类型的数据。
抽象方法:R apply(T t)
1 2 3 4 5 6 7 8 9 10
| public class FunctionDemo { public static void change(String s, Function<String, Integer> fun) { Integer i = fun.apply(s); System.out.println(i); } public static void main(String[] args) { change("56", (s)->Integer.parseInt(s)); } }
|
默认方法:default Function<T, V> andThen(Function<? super R, ? extends V> after)
1 2 3 4 5 6 7 8 9 10
| public class FunctionDemo { public static void change(String s, Function<String, Integer> fun1, Function<Integer, String> fun2) { String str = fun1.andThen(fun2).apply(s); System.out.println(str); } public static void main(String[] args) { change("56", (s)->Integer.parseInt(s),(i)->i+""); } }
|