1、函数式接口
1.1 函数式接口概述
函数式接口
有且只有一个抽象方法的接口,可以有其他方法,静态、默认、私有。。。
Java中的函数式编程体现就是lambda,所以函数式接口就是适用于lambda使用的接口,只有确保有且只有一个抽象方法,lambda才能顺利地进行推导
语法糖是使用更方便但原理不变的代码语法,增强for就是迭代器的语法糖
在应用层面上,lambda可以看作是匿名内部类的语法糖,但二者原理不同
用匿名内部类会生成对应的class文件,但lambda没有,所以lambda效率更高
使用注解@FunctionalInterface,可以检测接口是不是一个函数式接口,是则编译成功,否则编译失败(没有抽象方法或多于一个)
使用:作为方法的参数和返回值类型
public class Demo01FunctionalInterface {
public static void main(String[] args) {
//调用函数式接口作为参数的方法,参数传递其实现类对象
show(new MyFunctionalInterfaceImpl());
//调用函数式接口作为参数的方法,参数传递其匿名内部类
show(new MyFunctionalInterface() {
@Override
public void method() {
System.out.println("使用匿名内部类重写接口中的抽象方法");
}
});
//调用函数式接口作为参数的方法,参数使用lambda
show(() -> {
System.out.println("使用lambda重写接口中的抽象方法");
});
//简化lambda
show(() -> System.out.println("简化后的lambda"));
}
public static void show(MyFunctionalInterface myinterface) {
myinterface.method();
}
}
1.2 存在性能浪费的日志案例
日志可以帮助我们快速定位问题,记录程序执行过程中的情况,以便项目的监控和优化
以下代码存在性能浪费问题,调用log方法,参数传递拼接后的字符串,会先进行字符串拼接再调用方法,如果等级不为1就白拼接了
public class Demo02Logger {
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);
}
}
1.3 使用lambda优化的日志案例
lambda特点:延迟加载
lambda使用前提:必须存在函数式接口
使用lambda表达式作为参数传递,仅仅是将参数传递到show方法中,只有满足等级为1的条件,才会调用MergeMessage接口中的merge方法进行字符串拼接
//拼接字符串的函数式接口
@FunctionalInterface
public interface MergeMessage {
//拼接字符串的方法
public abstract String merge();
}
public class Demo03LoggerImprove {
public static void main(String[] args) {
String msgA = "Hello";
String msgB = "World";
String msgC = "Java";
show(1,() -> {
System.out.println("满足条件执行该方法体");
return msgA + msgB + msgC;
});
show(2,() -> {
System.out.println("不满足条件不执行该方法体");
return msgA + msgB + msgC;
});
//简化后
show(1,() -> msgA + msgB + msgC);
}
//定义显示日志的方法,参数传递日志等级和定义的字符串拼接的函数式接口
private static void show(int level, MergeMessage m) {
if (level == 1) {
//打印拼接好的字符串
System.out.println(m.merge());
}
}
}
1.4 函数式接口作为方法参数
使用函数式接口Runnable作为方法参数,就可以使用lambda表达式进行传参,与Thread的构造方法参数为Runnable的情况没有区别
public class Demo04FunctionalInterfaceAsPara {
public static void main(String[] args) {
//使用匿名内部类
startThread(new Runnable() {
@Override
public void run() {
System.out.println("线程" + Thread.currentThread().getName() + "启动了");
}
});
startThread(() -> {
System.out.println("线程" + Thread.currentThread().getName() + "启动了");
});
//lambda简化后
startThread(() -> System.out.println("线程" + Thread.currentThread().getName() + "启动了"));
}
public static void startThread(Runnable r) {
new Thread(r).start();
}
}
1.5 函数式接口作为方法返回值
函数式接口作为方法返回值类型,可以直接返回一个lambda表达式
本例使用比较器Comparator接口作为方法返回值类型
public class Demo05FunctionalInterfaceAsReturn {
public static void main(String[] args) {
String[] arr = {"aaa", "o", "dd"};
System.out.println(Arrays.toString(arr));//[aaa, o, dd]
Arrays.sort(arr, getComparator());
System.out.println(Arrays.toString(arr));//[aaa, dd, o]
}
public static Comparator<String> getComparator() {
//返回匿名内部类
// return new Comparator<String>() {
// @Override
// public int compare(String o1, String o2) {
// //按照字符串长度降序排序,后-前
// return o2.length() - o1.length();
// }
// };
//返回lambda表达式
// return (o1, o2) -> {
// return o2.length() - o1.length();
// };
//优化后
return (o1, o2) -> o2.length() - o1.length();
}
}
1.6 自定义函数式接口
//自定义的函数式接口
@FunctionalInterface
public interface MyFunctionalInterface {
public abstract void method();
//void method2();//有一个以上的抽象方法或没有抽象方法就会注解编译报错
}
//接口实现类
public class MyFunctionalInterfaceImpl implements MyFunctionalInterface{
@Override
public void method() {
System.out.println("实现类重写接口中的抽象方法");
}
}
2、常见的函数式接口
都在java.util.function包中
2.1 Supplier
Supplier接口
方法:T get();接口指定什么类型的泛型,就返回什么类型的数据
生产型接口 接口指定什么类型的泛型,接口中的get方法就会生产什么类型的数据
public class Demo01Supplier {
//定义一个方法,参数传递Supplier<T>接口
public static String getString(Supplier<String> supplier) {
return supplier.get();
}
public static void main(String[] args) {
//使用匿名内部类
String s1 = getString(new Supplier<String>() {
@Override
public String get() {
//生产一个字符串并返回
return "匿名内部类";
}
});
System.out.println(s1);
//使用lambda表达式
String s2 = getString(() -> {
return "lambda";
});
System.out.println(s2);
//优化后的lambda表达式 省略return 分号 大括号
String s3 = getString(() -> "优化后的lambda表达式");
System.out.println(s3);
}
}
2.2 Supplier练习:求数组最大值
使用Supplier接口作为方法参数类型,通过lambda表达式求出int数组元素最大值
提示:接口泛型为Integer
public class Demo02SupplierMax {
//定义方法,参数传递Supplier接口,泛型为Integer
public static int max(Supplier<Integer> supplier) {
return supplier.get();
}
public static void main(String[] args) {
//定义int数组
int[] array = {23, 54, 3, 22, 6};
//调用max函数,间接调用Supplier接口的get函数
int max = max(() -> {
int m = array[0];
for (int i = 1; i < array.length; i++) {
if (m < array[i]) {
m = array[i];
}
}
return m;
});
System.out.println(max);//54
}
}
2.3 Consumer
Consumer接口
方法:
void accept(T t);消费一个指定泛型的数据
Consumer andThen(Consumer<? super T> after);将两个Consumer接口连接在一起,再消费数据
Consumer con1;
Consumer con2;
con1.andThen(con2).accept(t);谁在前面谁先消费
消费型接口 与Supplier接口相反 用于消费数据 数据类型由泛型决定
public class Demo03Consumer {
//accept方法
//定义方法,参数传递字符串的姓名和Consumer接口,泛型使用String类型,可以使用Consumer接口消费字符串的姓名
public static void method(String name, Consumer<String> consumer) {
consumer.accept(name);
}
//andThen方法
//定义方法,参数传递字符串和两个Consumer接口
public static void method2(String name, Consumer<String> con1, Consumer<String> con2) {
con2.andThen(con1).accept(name);//先执行con1的accept再执行con2的accept
}
public static void main(String[] args) {
String s = "lambda";
method(s, (String name) -> {
//消费方式:直接输出
System.out.println(name);//lambda
//消费方式:反转输出
String nameReverse = new StringBuilder(name).reverse().toString();
System.out.println(nameReverse);//adbmal
});
method2(s, (String name) -> {
System.out.println(name.toUpperCase());//LAMBDA
}, (String name) -> {
System.out.println(name.toLowerCase());//lambda
});
}
}
2.4 Consumer练习:格式化打印信息
按照格式"姓名:**。性别:。"打印信息
public class Demo04ConsumerPrint {
//定义方法,参数传递字符串数组和两个Consumer接口
public static void method(String[] array, Consumer<String> con1, Consumer<String> con2) {
for (String s : array) {
con1.andThen(con2).accept(s);
}
}
public static void main(String[] args) {
String[] people = {"张三,男", "李四,女", "王五,男"};
method(people, (String s) -> {
String name = s.split(",")[0];
System.out.print("姓名:" + name + "。");
}, (String s) -> {
String sex = s.split(",")[1];
System.out.println("性别:" + sex + "。");//姓名:张三。性别:男。
//姓名:李四。性别:女。
//姓名:王五。性别:男。
});
}
}
2.5 Predicate
Predicate接口
方法:boolean test(T t);对某种数据类型的值进行判断
Predicate and(Predicate<? super T> other);连接两个判断条件,表示并且关系
public class Demo05Predicate {
//test方法
//定义方法,参数传递字符串和Predicate接口,使用接口中的test方法对字符串进行判断,并将判断结果返回
public static boolean method(String s, Predicate<String> predicate) {
return predicate.test(s);
}
//and方法
//判断字符串长度是否大于5且包含a
public static boolean method2(String s, Predicate<String> pre1, Predicate<String> pre2) {
return pre1.and(pre2).test(s);
}
//or方法
//判断字符串长度是否大于5或包含a
public static boolean method3(String s, Predicate<String> pre1, Predicate<String> pre2) {
return pre1.or(pre2).test(s);
}
//negate方法
//判断字符串长度是否大于5
public static boolean method4(String s, Predicate<String> pre) {
return pre.negate().test(s);
}
public static void main(String[] args) {
//test方法
// String s = "用于判断";
// boolean b = method(s, (String str) -> {
// if (str.length() > 5) {
// return true;
// } else {
// return false;
// }
// });
// System.out.println(b);//false
//and方法
// String s2 = "dbfhgs";
// boolean b2 = method2(s2, (String str) -> {
// if (s2.length() > 5) {
// return true;
// } else {
// return false;
// }
// }, (String str) -> {
// if (s2.contains("a")) {
// return true;
// } else {
// return false;
// }
// });
// System.out.println(b2);
//or方法
// String s3 = "afcs";
// boolean b3 = method3(s3, (String str) -> {
// if (s3.length() > 5) {
// return true;
// } else {
// return false;
// }
// }, (String str) -> {
// if (s3.contains("a")) {
// return true;
// } else {
// return false;
// }
// });
// System.out.println(b3);
//negate方法
String s4 = "fgh";
boolean b4 = method4(s4, (String str) -> {
if (s4.length() > 5) {
return true;
} else {
return false;
}
});
System.out.println(b4);//大于5true,取反false;小于5false,取反true
}
}
2.6 Predicate练习:将字符串数组中符合条件的信息筛选到ArrayList集合中
条件:必须为女生且名字为两个字
public class Demo06PredicateSelect {
public static void select(String[] arr, Predicate<String> pre1, Predicate<String> pre2) {
ArrayList<String> list = new ArrayList<>();
for (String s : arr) {
if (pre1.and(pre2).test(s)) {
list.add(s);
}
}
System.out.println(list);
}
public static void main(String[] args) {
String[] arr = {"张三,女", "李四百,女", "王五,男"};
select(arr, (String str) -> {
String name = str.split(",")[0];
if (name.length() == 2) {
return true;
} else {
return false;
}
}, (String str) -> {
String sex = str.split(",")[1];
if (sex.equals("女")) {
return true;
} else {
return false;
}
});
}
}
2.7 Function
Function<T,R>接口
方法:R apply(T t);根据类型T的参数获取类型R的结果
Function<T,V> andThen(Function<? super R, ? extends V> after);
public class Demo07Function {
//apply方法,将字符串类型的整数转换为Integer类型的整数
public static void method(String num, Function<String, Integer> fun) {
int n = fun.apply(num);
System.out.println(n);//123
}
//andThen方法,将字符串类型的数据转换为整型,加上10,再转换为字符串
public static void method2(String num, Function<String, Integer> fun1, Function<Integer, String> fun2) {
String s = fun1.andThen(fun2).apply(num);
System.out.println(s);//149
}
public static void main(String[] args) {
// String s = "123";
// method(s, (String str) -> {
// return Integer.parseInt(str);
// });
String s = "139";
method2(s, (String str) -> {
//将字符串转换为整数
return Integer.parseInt(str) + 10;
}, (Integer i) -> {
//将整数转换为字符串
return i + "";
});
//优化后
method2(s, str -> Integer.parseInt(str) + 10, i -> i + "");
}
}
2.8 Function练习:将字符串中的年龄部分截取出来String->String,转换为整型String->Integer,再加10Integer->Integer
public class Demo08FunctionPra {
public static void method(String s, Function<String, String> fun1,Function<String, Integer> fun2, Function<Integer, Integer> fun3) {
int num = fun1.andThen(fun2).andThen(fun3).apply(s);
System.out.println(num);//28
}
public static void main(String[] args) {
String s = "张三,18";
method(s, (String str) -> {
return str.split(",")[1];
}, (String str) -> {
return Integer.parseInt(str);
}, (Integer i) -> {
return i + 10;
});
}
}