目录
1. 介绍
Java中的函数式接口指的是:有且仅有一个抽象方法的接口。(通常接口上方都会有@FunctionInterface注解,用于在编译期发现错误)
Java函数式接口的体现就是Lambda表达式,所有函数式接口都是适用于Lambda表达式使用的接口。
1.1 @FunctionInterface注解
@FunctionalInterface // 表明该类为函数式接口
public interface DoubleSupplier {
double getAsDouble();
}
只要在类上加上@FunctionInterface注解,则告诉了编译器该类为函数式接口,只要不符合函数接口的只有一个抽象方法的规范,就会报错。
注意:即使不加@FunctionInterface接口,只有一个抽象方法的类,也能算是函数是接口,只是编译器不知道。
1.2 函数式接口的调用
public class TestFunInterface2 {
//需求:使用Consumer接口作为方法的参数,对字符串实现翻转
public static void revString(String str, Consumer<String> consumer){
consumer.accept(str);
}
public static void main(String[] args) {
revString("zhangsan",(str)->{
StringBuilder reverseStr = new StringBuilder(str).reverse();
System.out.println("翻转后的字符串:"+reverseStr);
});
}
}
2. 函数式编程
2.1 Lambda的延迟加载技术
通过log4j的日志打印功能举例:
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);// 无论是否满足log级别为1,都会执行字符串拼接,造成性能浪费。
}
}
经过Lambda表达式的优化后:只有满足日志级别为2,才能进入lambda表达式,进行字符拼接。
@FunctionalInterface
public interface MessageBuilder {
String buildMessage();
}
public class Demo02LoggerLambda {
private static void log(int level, MessageBuilder builder) {
if (level == 1) {
// 实际上利用内部类 延迟的原理,代码不相关 无需进入到启动代理执行
System.out.println(builder.buildMessage());
}
}
public static void main(String[] args) {
String msgA = "Hello";
String msgB = "World";
String msgC = "Java";
log(2,()->{
System.out.println("lambda 是否执行了");
return msgA + msgB + msgC;
});
}
}
2.2 Lambda表达式的使用
- Lambda表达式作为参数
public class Runnable {
private static void startThread(Runnable task) {
new Thread(task).start();
}
public static void main(String[] args) {
startThread(() ‐> System.out.println("线程任务执行!"));
}
}
- Lambda表达式作为返回值
public class lambda_Comparator {
private static Comparator<String> newComparator1(){
return new Comparator<String>() {
@Override
public int compare(String a, String b) {
return b.length()-a.length();
}
};
}
public static void main(String[] args) {
String[] array={"abc","ab","abcd"};
Arrays.sort(array, newComparator1()); // 方式一:复杂写法
Arrays.sort(array,(a,b)->b.length()-a.length());//更简单的方式
System.out.println(Arrays.toString(array));
}
}
3. 常用的函数式接口
3.1 Supplier生产型接口
java.util.function.Supplier<T>
接口是生产一个数据,其数据类型由泛型T来定。
Supplier
接口仅包含一个无参的方法: T get()
。用来获取一个泛型参数指定类型的对象数据。
public class Test_Supplier {
private static String test_Supplier(Supplier<String> suply) {
return suply.get(); //供应者接口
}
public static void main(String[] args) {
// Lambda表达式写法:产生的数据作为 sout 作为输出
System.out.println(test_Supplier(()->"产生数据"));
// 匿名内部类写法:
System.out.println(String.valueOf(new Supplier<String>() {
@Override
public String get() {
return "产生数据";
}
}));
}
}
3.2 Consumer消费型接口
java.util.function.Consumer<T>
接口是消费一个数据,其数据类型由泛型决定。
Consumer
接口中包含抽象方法 void accept(T t)
,表示消费一个指定泛型的数据。基本使用如:
public class Test_Comsumer {
public static void generateX(Consumer<String> consumer) {
consumer.accept("hello consumer");
}
public static void main(String[] args) {
generateX(s->System.out.println(s));
}
}
默认方法:andThen
将两个Consumer组合,先消费一条数据,随后再消费一条数据。
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) ‐> { accept(t); after.accept(t); };
//1: 返回值为Consumer 那么需要 ()-> 表示函数式接口
//2: accept(t);为生产一个数据供应给 (T t)中的t
//3: after.accept(t);为利用这个t再次生成新的函数式接口 实现类始于builder的设计模式
}
例如:
public class TestFunInterface3 {
public static void method(String s, Consumer<String> con1,Consumer<String> con2){
//con1.accept(s);
//con2.accept(s);
con1.andThen(con2).accept(s); //谁在前面谁先消费
}
public static void main(String[] args) {
method("zhangsan",(str)->{
System.out.println(str.toUpperCase());
},(str)->{
System.out.println(str.toLowerCase());
});
}
}
3.3 Predicate条件判断接口
java.util.function.Predicate<T>
用于对某种类型的数据进行判断,并得到一个boolean值结果。(即是生产者,又是消费者)
抽象方法: boolean test(T t)
用于条件判断的场景:
默认方法: and(与)、or(或)、nagte(取反)
既然是条件判断,就会存在与、或、非三种常见的逻辑关系。
其中将两个 Predicate 条件使用“与”逻辑连接起来实现“并且”的效果时,类始于 Consumer接口 andThen()函数 其他三个雷同
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) ‐> test(t) && other.test(t);
}
静态方法: isEquals
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
举例:
public class Use_Predicate {
// 判断字符串是否存在o,即是生产者 又是消费者接口
private static void method_test(Predicate<String> predicate) {
boolean b = predicate.test("OOM SOF");
System.out.println(b);
}
// 判断字符串是否同时存在o和h
private static void method_and(Predicate<String> predicate1,Predicate<String> predicate2) {
boolean b = predicate1.and(predicate2).test("OOM SOF");
System.out.println(b);
}
// 判断字符串是否存在o或者h
private static void method_or(Predicate<String> predicate1,Predicate<String> predicate2) {
boolean b = predicate1.or(predicate2).test("OOM SOF");
System.out.println(b);
}
// 判断字符串是否存在o,结果取反
private static void method_negate(Predicate<String> predicate) {
boolean b = predicate.negate().test("OOM SOF");
System.out.println(b);
}
public static void main(String[] args) {
method_test((s)->s.contains("O"));
method_and(s->s.contains("O"), s->s.contains("h"));
method_or(s->s.contains("O"), s->s.contains("h"));
method_negate(s->s.contains("O"));
}
}
3.4 Function普通函数接口
java.util.function.Function<T,R>
接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。
简单来说就是:将 T
转为 R
返回
// 将数字转换为String类型
private static void numberToString(Function<Number, String> function) {
String apply = function.apply(12);
System.out.println("转换结果:"+apply);
}
public static void main(String[] args) {
numberToString((s)->String.valueOf(s));
}
默认方法:andThen compose()
区别: 执行顺序不同,andThen是调用者先执行,传入的function后执行;compose是传入的function先执行,调用者后执行。
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
// 先执行调用者,再执行after的apply方法
return (T t) -> after.apply(apply(t));
}
// 这里的V 一个是作为输入值 一个是作为输出值 按照调用的顺序的不同 对于 T V 做输入 输出的顺序也不同 注意看
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
// 先执行before的apply方法,后执行调用者apply方法
return (V v) -> apply(before.apply(v));
}
注意调用的先后顺序:
// 静态方法
private static void method_andThen(Function<Integer, Integer> f1, Function<Integer, Integer> f2) {
// andThen:先执行f1,再执行f2
Integer apply = f1.andThen(f2).apply(2);
System.out.println(apply);
}
private static void method_compose(Function<Integer, Integer> f1, Function<Integer, Integer> f2) {
// compose:先执行f2,再执行f1
Integer apply = f1.compose(f2).apply(2);
System.out.println(apply);
}
public static void main(String[] args) {
method_andThen(s -> s + 1, s -> s = s * 2);//6
method_compose(s -> s + 1, s -> s = s * s);//5
}
3.5 Operator 同类型相互转换接口
个人理解: 例如对字符串操作,返回字符串类型。像Stream流中的map方法,但是限定了返回值和参数相同。
BinaryOperator<Integer>
的 andthen()
方法不支持两个函数链接操作 也就是不需要再次 BinaryOperator<Integer>
因为源代码规定不允许使用两次输入。
public static void main(String[] args) {
// 单个同类型操作
UnaryOperator<String> u_str=(s)->s.split(" ")[0];
UnaryOperator<String> u_str1=(s)->s.concat(" ok");
String apply = u_str.andThen(u_str1).apply("lambda Ok");
String apply1 = u_str.compose(u_str1).apply("lambda Ok");
System.out.println(apply);
System.out.println(apply1);
// 两个同类型操作
BinaryOperator<Integer> way_add=(k, v)->k+v;
BinaryOperator<Integer> way_mul=(k,v)->k*v;
// 注意不能连写!
Integer res1 = way_add.apply(1,2);
Integer res2 = way_mul.apply(1,2);
System.out.println(res1);
System.out.println(res2);
}