网站首页 > 精选教程 正文
读书要靠一点一滴的积累,切莫贪多求快,贪多往往嚼不烂。
引言
java版本更新的速度非常快,印象最深的版本还是1.8版本,引入了函数式的编程理念,本文结合实际使用案例,重学java8。重学的主要目的有两个:
1.简化业务代码,使代码更简洁优雅(行数越少,看着越清晰明了,也是开发者所追求的)
2.看懂源码(越来越多的框架源码到处都是函数式,方法引用,看源码绕不过去的大山)
先简单看下源码用到java8语法的地方
先看效果
正所谓,有目标才有动力嘛,先来对比一下java8与之前版本的效果图
需求:给集合按用户年龄大小排序,并打印
上代码:
public static void main(String[] args) {
List<User> list = new ArrayList<>();
list.add(new User("张三", 12, "红色"));
list.add(new User("李四", 20, "蓝色"));
list.add(new User("王五", 19, "红色"));
list.add(new User("赵六", 17, "蓝色"));
list.add(new User("钱八", 18, "绿色"));
/** 需求: 根据年龄大小排序集合并打印 ***/
// 1.老版实现方式,先排序
list.sort(new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
return o1.getAge() - o2.getAge();
}
});
// 再打印
for (User user : list) {
System.out.println(user);
}
// java8 实现方式
list.sort(Comparator.comparing(User::getAge));
list.forEach(System.out::println);
}
问题来了,倒序呢,如何实现?
// 老版 对比的对象换个位置就行
list.sort(new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
return o2.getAge() - o1.getAge();
}
});
// java8 的方式也简洁,把正序翻转一下就是了
list.sort(Comparator.comparing(User::getAge).reversed());
不论是从代码的简洁性上,还是逻辑性上,java8明显的更具优势。
概念学习
java8 引入了函数式编程概念,怎么理解呢,以往我们定义方法时,参数的定义,都是基础类型或者Object等类型,如:
public void print (int a, String b, Object c, User d) {}
现在呢,我想传递一组行为,或者动作,也可以理解为一组业务逻辑,如先打印,再计算
动作1 = { 1.打印 2.计算求和 } 动作2 = { 1.求平均值 2.调用其他函数 }
public void print(Function a, Predicate b) {....}
函数式的就是把 动作1 和 动作2 也当做参数,也能传递,为此呢,java引入了函数式接口来定义上述的动作等。总体来说引入了三大类:
- Predicate:接口定义了一个名叫test的抽象方法,它接受泛型 T对象,并返回一个boolean
- Consumer:定义了一个名叫accept的抽象方法,它接受泛型T 的对象,没有返回(void)
- Function:接口定义了一个叫作apply的方法,它接受一个 泛型T的对象,并返回一个泛型R的对象
java8 将动作分成了三类:
Predicate 分类就是这个动作会返回一个boolean类型的值(可以理解为条件判断动作)
当我们调用preicate类型的test方法时就会返回一个boolean类型值
Consumer 属于没有返回值的动作
当我们调用consumer类型的accept方法时,就会执行动作,并无返回值。
Function 属于有返回值的动作
当我们调用function类型的apply方法时,会返回一个类型的值。
实战演示
Predicate类型demo演示:
先定义一个接受predicate的方法,该方法接受动态表达式判断,若传入的参数校验成功,则打印 hello world,否则打印 code is beautiful
定义了三组函数式动作,并调用方法,看下效果图
上代码:
public static <T> void printWorldOption(Predicate<T> predicate, T param) {
boolean test = predicate.test(param);
// 如果条件为真,那么我们就打印一下 hello world 也可执行一组其他的动作
if (test) {
System.out.println("hello world");
} else {
System.out.println("code is beautiful");
}
}
public static void main(String[] args) {
// 条件: 字符串比较abc
Predicate<String> condition1 = x -> x.equals("abc");
printWorldOption(condition1, "abcd");
// 条件:字符传长度是否大于5
Predicate<String> condition2 = x -> x.length() > 5;
printWorldOption(condition2, "12312321d");
// 条件:字符串中是否包含a
Predicate<String> condition3 = x -> x.contains("a");
printWorldOption(condition3, "abcd");
}
Consumer类型demo演示:
先定义一下执行动作的方法,里边也可以加一些其他动作,演示demo简洁为主
虽然调用的都是一个方法,可以看到,传入的动作与参数都不一致,打印的也是依据动作来的。
上代码:
public static <T> void printWorld(Consumer<T> consumer, T param1) {
consumer.accept(param1);
}
public static void main(String[] args) {
// 打印 “你好”
Consumer<String> consumer1 = System.out::println;
printWorld(consumer1, "你好");
// 获取字符串长度,并想加打印
Consumer<String> consumer2 = str -> {
int a = str.length();
int b = a + a;
System.out.println(b);};
printWorld(consumer2, "helloworld");
// 数子先加1,再乘以9, 最后打印
Consumer<Integer> consumer3 = a -> {
a += 1;
a *= 9;
System.out.println(a);};
printWorld(consumer3, 2);
}
Function类型的也是一个道理,只是有返回值。暂时先不做演示了,直接给大家看看真正实际应用的框架源码,和作者日常开发用到的情况。
mybatis-plus的源码应用
方法太长了截图看不全,上代码吧
public static <E> boolean saveOrUpdateBatch(Class<?> entityClass, Class<?> mapper, Log log, Collection<E> list, int batchSize, BiPredicate<SqlSession, E> predicate, BiConsumer<SqlSession, E> consumer) {
String sqlStatement = getSqlStatement(mapper, SqlMethod.INSERT_ONE);
return executeBatch(entityClass, log, list, batchSize, (sqlSession, entity) -> {
if (predicate.test(sqlSession, entity)) {
sqlSession.insert(sqlStatement, entity);
} else {
consumer.accept(sqlSession, entity);
}
});
}
这是一个批量的保存或更新的一个方法, 该方法定义了Predicate的条件型函数,和一个Consumer消耗性函数,BiPredicate、BiConsumer, 小伙伴不要被吓到,这两个看着像,但是和我们讲的不太一样啊,前边多了Bi啊,其实这是同族的演化,就是可以多传入参数,比Predicate,Consumer多传入一个入参,用法一样的。
大体上可以看到,这个predicate是为了判断是否插入还是更新的一个条件。追溯一下调用的地方
可以看到,划线的地方,就是predicate的具体动作判断, 方框地方,就是consumer的动作。上代码:
return SqlHelper.saveOrUpdateBatch(this.entityClass, this.mapperClass, this.log, entityList, batchSize, (sqlSession, entity) -> {
Object idVal = tableInfo.getPropertyValue(entity, keyProperty);
return StringUtils.checkValNull(idVal)
|| CollectionUtils.isEmpty(sqlSession.selectList(getSqlStatement(SqlMethod.SELECT_BY_ID), entity));
}, (sqlSession, entity) -> {
MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
param.put(Constants.ENTITY, entity);
sqlSession.update(getSqlStatement(SqlMethod.UPDATE_BY_ID), param);
});
作者在项目中用到如下图所示,因为项目代码的保密性,做了抽离。
重点总结
理解函数式非常重要,这是我们用好java8的前提基础,简便的理解函数就是一组动作,动作也有分类,大体分了三类,理解了这三类函数式接口,剩下的都是小菜了。最后再补充一个知识点,就是我们也可以自定义函数式接口,加上一个注解就可以了@FunctionalInterface ,需特别注意,函数式只能有一个抽象方法奥。
猜你喜欢
- 2024-12-01 我的世界:13个关于1.17的小变化,却可能对未来mc产生巨大影响
- 2024-12-01 编程专题:初识JAVA(以java1.8为例)
- 2024-12-01 从 Java 8 升级到 Java 17 全过程,贼特么坑
- 2024-12-01 Java 状态报告:Java 8 占主导,Java 11 不算多
- 2024-12-01 Java8已经发布7年了,不会还有人没用过CompletableFuture吧
- 2024-12-01 我的世界:老mc收藏多年的干货,教你寻找最罕见的11个遗迹
- 2024-12-01 从 Java 8 转换到 Java 11
- 2024-12-01 Java 8 开始退场
- 2024-12-01 我的世界:12颗满眼末地门多稀有?大神翻遍mc,找到900万个种子
- 2024-12-01 学妹惊呼:使用Java8改造后的模板方法模式真的是yyds
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- nginx反向代理 (57)
- nginx日志 (56)
- nginx限制ip访问 (62)
- mac安装nginx (55)
- java和mysql (59)
- java中final (62)
- win10安装java (72)
- java启动参数 (64)
- java链表反转 (64)
- 字符串反转java (72)
- java逻辑运算符 (59)
- java 请求url (65)
- java信号量 (57)
- java定义枚举 (59)
- java字符串压缩 (56)
- java中的反射 (59)
- java 三维数组 (55)
- java插入排序 (68)
- java线程的状态 (62)
- java异步调用 (55)
- java中的异常处理 (62)
- java锁机制 (54)
- java静态内部类 (55)
- java怎么添加图片 (60)
- java 权限框架 (55)
本文暂时没有评论,来添加一个吧(●'◡'●)