day12 函数式接口今日内容 自定义函数式接口 @FunctionalInterface 注解
Lambda表达式:
Lambda表达式的"延迟执行"的特点
函数式接口作为方法的"参数"和"返回值类型"
常用函数式接口
Supplier: 生产型函数式接口 获取值
Consumer: 消费型函数式接口 使用值
Predicate: 条件判断型函数式接口 判断值
Function: 转换型函数式接口 转换值函数式接口函数式接口概念, 自定义函数式接口知识点: 什么是函数式接口
如何自己定义一个函数式接口
@FunctionalInterface 注解有什么作用总结: 函数式接口: JDK 8 新特性
有且仅有一个抽象方法的接口, 适用于函数式编程场景的接口
(默认方法, 静态方法, 私有方法, 与 java.lang.Object 类中定义相同的抽象方法, 都不算作抽象方法)
自定义函数式接口:
接口中有且只有一个抽象方法
@FunctionalInterface的作用:
在接口上使用, 检测当前的接口是否为函数式接口补充: // 格式
修饰符?interface?接口名称?{
public?abstract?返回值类型?方法名称(可选参数信息);
//?其他非抽象方法内容
}
// 示例
@FunctionalInterface
public interface MyFunctionalInterface {
void method();
}函数式接口的使用知识点: 定义了函数式接口后, 如何使用它总结: 函数式接口的使用:
作为方法的参数类型, 传递Lambda表达式, 代替匿名内部类方式补充: public static void method(Animal a) { // 传递实现类对象 Animal a = new Cat();
a.eat();
}
public static void method(Runnable r) { // 传递实现类对象 Runnable r = new Runnable(){}
new Thead(r).start();
}函数式编程性能浪费的日志案例知识点: 演示代码的方法中, 传递的字符串参数是否需要先拼接, 才能传入方法内部总结: 日志: 就是存储程序运行信息的文本文件.
我们平时打印输出一些信息是在控制台上, 如果将这些信息写入文本文件, 这些文件就是日志
public static void showLog(int level, String message) {
if (level == 1) {
System.out.println(message);
}
}
showLog(2, s1 + s2 + s3);
// 先拼接s1+s2+s3出结果"helloworldjava", 然后才执行方法内部的判断
// 相当于方法不需要执行, 但仍然在拼接字符串时浪费了一些性能补充: 使用Lambda延迟执行的特性优化日志案例知识点: 使用Lambda实现相同功能, 能否提高效率总结: Lambda具有"延迟执行"的特点:
传递Lambda对象, 只有当符合执行条件时, 才会执行代码
@FunctionalInterface
interface MessageBuilder {
String buildMessage();
}
public static void showLog(int level, MessageBuilder builder) { // 传递实现类对象
if (level == 1) {
String s = builder.buildMessage();
System.out.println(s);
}
}
showLog(1, new MessageBuilder(){
@Override
public String buildMessage() {
return s1 + s2 + s3;
}
});
showLog(2, () -> s1+s2+s3);
// 传递Lambda对象, 即MessageBuilder实现类对象进方法
// 判断2和1不相等, 所以不会执行builder.buildMessage()
// 从而也不会执行重写方法中的s1+s2+s3
// 避免了无意义的性能浪费补充: 5分钟练习: 对比Lambda和普通方式执行效率 需求:
定义函数式接口 MessageBuilder, 使用@FunctionalInterface注解修饰
定义抽象方法用于拼接字符串: String buildMessage();
定义测试类:
定义方法模拟拼接大量字符串:
public static String getString() {
// 模拟拼接字符串的耗时
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String s1 = "hello";
String s2 = "world";
String s3 = "java";
return s1 + s2 + s3;
}
定义方法: public static void showLog(int level, String message)
方法内部判断如果level==1, 则打印输出message
定义方法: public static void showLogLambda(int level, MessageBuilder builder)
方法内部判断如果level==1, 则打印输出builder.buildMessage()返回的字符串
定义main方法:
调用 showLog(2, getString()), 在方法调用前后获取毫秒值, 相减得出执行时间, 打印出来
调用 showLogLambda(2, ()->getString());在方法调用前后获取毫秒值, 相减得出执行时间, 打印出来代码: public interface MessageBuilder {
// 用于拼接字符串的方法, 怎么拼, 需要我们传递Lambda来实现
String buildMessage();
}
public class Test {
// 模拟拼接大量字符串的方法:
public static String getString() {
// 模拟拼接字符串的耗时
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String s1 = "hello";
String s2 = "world";
String s3 = "java";
return s1 + s2 + s3;
}
// 普通方式
public static void showLog(int level, String message) {
if (level == 1) {
System.out.println(message);
}
}
// 使用Lambda的延迟执行特性
public static void showLogLambda(int level, MessageBuilder builder) {
if (level == 1) {
String s = builder.buildMessage();
System.out.println(s);
}
}
public static void main(String[] args) {
// 调用普通方法
long start1 = System.currentTimeMillis();
showLog(2, getString());
long end1 = System.currentTimeMillis();
System.out.println("普通方式耗时:" + (end1-start1));
// 调用Lambda延迟执行的方法
long start2 = System.currentTimeMillis();
showLogLambda(2, ()->getString());
long end2 = System.currentTimeMillis();
System.out.println("延迟执行方式耗时:" + (end2-start2));
}
}使用Lambda表达式作为方法参数知识点: 当一个方法的参数是一个函数式接口时, Lambda表达式方式是否比匿名内部类方式简单
... startThread(Runnable r){ // submit(Runnable r)
new Thread(r).start();
}总结: 当一个方法的参数是一个函数式接口时, 可以使用Lambda表达式传递该参数, 简化匿名内部类的代码补充: 5分钟练习: 函数式接口作为方法参数 需求:
定义方法, 用来快速开启线程执行一个任务: public static void startThread(Runnable r)
方法内部创建一个线程对象, 并将任务传递, 开启线程: new Thread(r).start();
在main方法中调用startThread()方法, 使用Lambda表达式方式, 传入一个任务, 任务中打印线程名+"线程启动了"代码: public class Test {
public static void main(String[] args) {
// 匿名内部类方式调用方法传递参数
startThread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程启动了");
}
});
// Lambda标准格式传递参数
startThread(()->{
System.out.println(Thread.currentThread().getName() + "线程启动了");
});
// Lambda省略格式传递参数
startThread(()->System.out.println(Thread.currentThread().getName() + "线程启动了"));
}
// 练习调用参数是函数式接口的方法, 可以向参数传递Lambda表达式
public static void startThread(Runnable r) {
new Thread(r).start();
}
}
使用Lambda表达式作为方法返回值知识点: 接口: 可以作为方法的参数类型, 还可以作为方法的返回值类型
当一个方法的返回值是一个函数式接口时, Lambda表达式方式是否比匿名内部类方式简单
public static Comparator<String> getComparator(){
return new Comparator<String>(){
@Override
public int compare(String o1, String o2) {
// 编写比较的规则 升序
return o1.length()-o2.length();
}
};
}总结: 当一个方法的返回值是一个函数式接口时, 可以返回Lambda表达式, 简化匿名内部类的代码补充: 5分钟练习: 使用Lambda表达式作为返回值 需求:
定义方法, 用于获取一个比较器对象: public static Comparator<String> getComparator()
方法内部使用Lambda表达式作为Comparator接口的返回值, 比较规则是:
第二个字符串参数的长度 - 第一个字符串参数的长度
在main方法中
定义字符串数组: {"aaa", "b", "cccccc", "ddddddddddd"}
调用 getComparator() 方法获取比较器对象
调用 Arrays.sort(数组, 比较器) 方法, 将字符串数组排序, 然后打印出来代码: public class Test {
// 获取字符串比较器的方法
public static Comparator<String> getComparator() {
// 使用匿名内部类方式
/*return new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.length() - o1.length(); // 降序
}
};*/
// Lambda标准格式
/*return (String o1, String o2)->{
return o2.length() - o1.length(); // 降序
};*/
// Lambda省略格式
return (o1, o2)->o2.length() - o1.length(); // 按照字符串长度降序
}
public static void main(String[] args) {
String[] arr = {"aaa", "b", "cccccc", "ddddddddddd"};
System.out.println("排序前:" + Arrays.toString(arr));
Arrays.sort(arr, getComparator());
System.out.println("排序后:" + Arrays.toString(arr));
}
}常用函数式接口 Java中提供的常用 函数式接口:
Supplier<T>: 生产型函数式接口 获取值 a和b的和, 获取数组最大值, 获取一个Person对象
Consumer<T>: 消费型函数式接口 使用值 给它一个String让他打印, 给它一个对象
Predicate<T>: 条件判断型函数式接口 判断值 判断字符串长度是否大于5
Function<T, R>: 转换型函数式接口 转换值 给它一个String转换为Integer "123"->123Supplier生产型函数式接口知识点: JDK 提供的函数式接口在哪个包中
Supplier<T> 接口有什么作用总结: java.util.function.Supplier<T>函数式接口: 生产型函数式接口
// 抽象方法
T?get(): 用于获取一个对象或值.
至于获取什么值, 怎么获取, 需要我们根据应用场景编写Lambda来实现补充: public class Test {
public static void main(String[] args) {
// 调用方法
String s = getString( ()->"赵丽颖" );
System.out.println(s); // 赵丽颖
// 相当于
getString(new Supplier<String>(){
@Override
public String get() {
return "赵丽颖";
}
});
}
// 定义方法: 用来获取一个字符串 参数: 获取字符串的方式(代码)
public static String getString(Supplier<String> sup) { // 接口实现类对象
return sup.get();
}
}Supplier生产型函数式接口练习: 求数组最大值5分钟练习: 使用Supplier接口求数组最大值 需求:
使用Supplier接口作为方法参数类型,通过Lambda表达式求出int数组中的最大值
定义测试类:
定义方法, 用于获取数组中的最大值: public static int getMax(Supplier<Integer> sup)
方法内部直接调用sup对象的 get() 方法获取得到的结果, 并return
定义main方法:
方法中定义数组: {100, 0, -50, 88, 99, 33, -30}
调用 getMax() 方法, 方法内部传递Supplier接口的Lambda表达式
表达式中编写获取数组最大值的代码代码: public class Test {
// 方法的作用: 获取最大值 至于怎么获取, 获取什么的最大值, 需要我们传递Lambda表达式来实现
// 参数的作用: 获取最大值的方式(代码)
public static int getMax(Supplier<Integer> sup) {
return sup.get();
}
public static void main(String[] args) {
int[] arr = {100, 0, -50, 88, 99, 33, -30};
// 调用方法来获取数组中的最大值, 怎么获取呢? 我们来传递Lambda表达式实现
// 一开始不熟悉抽象方法的参数和返回值类型, 可以先写匿名内部类, 然后改写为Lambda
/*int maxValue = getMax(new Supplier<Integer>() {
@Override
public Integer get() {
int max = 0;
for (int i : arr) {
if (i > max) {
max = i;
}
}
return max;
}
});*/
// Lambda方式
int maxValue = getMax(()->{
int max = 0;
for (int i : arr) {
if (i > max) {
max = i;
}
}
return max;
});
System.out.println("最大值:" + maxValue);
}
}Consumer消费型函数式接口知识点: Consumer<T> 接口有什么作用总结: java.lang.StringBuilder类: 可变字符序列, 类似于String, 线程不安全效率高
StringBuilder reverse(): 将StringBuilder内部保存的内容反转
String toString(): 转换为String
new StringBuilder("abc").reverse().toString(); -> "cba" "abc" -> "cba"
java.lang.StringBuffer类: 可变字符序列, 类似于String, 线程安全效率低
StringBuffer reverse(): 将StringBuffer内部保存的内容反转
String toString(): 转换为String
java.util.function.Consumer<T>函数式接口: 消费型函数式接口
// 抽象方法
void?accept(T?t): 用于消费(使用)一个对象或值. 至于怎么消费, 要我们根据应用场景编写Lambda实现
// 默认方法
default?Consumer<T>?andThen(Consumer<??super?T>?after): 拼接两个Consumer接口的Lambda对象实现连续操作. 谁写前面, 谁先消费补充: public class Test {
// 定义方法用来消费一个字符串. 参数是 被消费的字符串 和 消费方式
public static void method(String name, Consumer<String> con){
con.accept(name);
}
public static void main(String[] args) {
String name = "赵丽颖";
// 消费这个字符串, 我们的消费方式是 反转字符串
method(name, (String name)->{
// 反转字符串
String reName = new StringBuilder(name).reverse().toString();
System.out.println(reName); // 颖丽赵
});
}
}Consumer消费型函数式接口: 默认方法andThen()知识点: Consumer<T>接口中的默认方法 Consumer<T> andThen(Consumer<??super?T>?after) 如何简化多个Consumer的消费总结: java.util.function.Consumer<T>函数式接口: 消费型函数式接口
// 默认方法
default?Consumer<T>?andThen(Consumer<??super?T>?after): 拼接两个Consumer接口的Lambda对象实现连续操作. 谁写前面, 谁先消费补充: public class Test {
// 定义方法用来消费一个字符串. 参数是 被消费的字符串 和 消费方式1 和 消费方式2
public static void method(String s, Consumer<String> con1, Consumer<String> con2){
// con1.accept(s);
// con2.accept(s);
// 使用andThen简化
con1.andThen(con2).accept(s);
}
public static void main(String[] args) {
// 传入两种消费方式
method("Hello",
(t) -> {
System.out.println(t.toUpperCase()); // HELLO
},
(t) -> {
System.out.println(t.toLowerCase()); // hello
});
}
}Consumer消费型接口练习: 字符串拼接输出5分钟练习: Consumer接口实现字符串拼接输出 需求:
字符串数组String[] array = {"迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男"};当中存有多条信息, 请按照格式"姓名:XX。性别:XX。"格式将信息打印出来.
要求:
将打印姓名的动作, 作为第一个Consumer接口的Lambda实例
将打印性别的动作, 作为第二个Consumer接口的Lambda实例
将两个Consumer接口按照顺序"拼接"到一起
public class Test {
public static void printInfo(String[] arr, Consumer<String> con1, Consumer<String> con2) {
// 补全代码
}
public static void main(String[] args) {
String[] array = {"迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男"};
// 调用printInfo方法
}
}
// 最终效果
姓名:迪丽热巴。性别:女。
姓名:古力娜扎。性别:女。
姓名:马尔扎哈。性别:男。
分析:
被消费的字符串s: "迪丽热巴,女"
// 消费方式1
String[] split = s.split(",");
String name = split[0];
System.out.print("姓名:"+name)
// 消费方式2
String[] split = s.split(",");
String gender = split[1];
System.out.println("。性别:"+gender+"。")
|
|