目录
- 1. 介绍
- 1.1 @FunctionInterface注解
- 1.2 函数式接口的调用
- 2. 函数式编程
- 2.1 Lambda的延迟加载技术
- 2.2 Lambda表达式的使用
- 3. 常用的函数式接口
- 3.1 Supplier生产型接口
- 3.2 Consumer消费型接口
- 默认方法:andThen
- 3.3 Predicate条件判断接口
- 3.4 Function普通函数接口
- 默认方法:andThen compose()
- 3.5 Operator 同类型相互转换接口
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>() {@Overridepublic 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>() {@Overridepublic 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,再执行f2Integer 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,再执行f1Integer 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);//6method_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);
}