J2SE-Jdk1.8
前言
Jdk1.8新特性:这里只介绍Optional、Lambda表达式、函数式接口、Stream API,其他特性例如Date Api等参考菜鸟文章。
Optional
最初对Optional的印象就是解决了空指针的问题,但是看了几篇文章感觉不实用,放弃。
后面学***A的过程中,经常碰到被Optional包装的返回结果,于是尝试着使用。目前最直接的感觉就是代码风格发生了变化。如下:
package com.huobi.examples;
import java.util.Objects;
import java.util.Optional;
/**
* 测试Optional
*
* @author hulei
* @date 2021/3/31
*/
public class OptionalTest {
/**
* 查询name,如果为null抛出异常
*
* @param args 参数
*/
public static void main(String[] args) {
// 对于第一种查询name方式的处理
String name = getUserName().orElseThrow(RuntimeException::new);
System.out.println(name);
// 对于第二种查询name方式的处理
String name2 = getUserName2();
if (Objects.isNull(name2)) {
throw new RuntimeException();
}
System.out.println(name2);
}
/**
* 第一种查询name的方式, 将可能为null的结果用Optional进行包装后返回
*
* @return name的包装
*/
public static Optional<String> getUserName() {
// 假定name通过数据库查询,可能为null
String name = "";
return Optional.ofNullable(name);
}
/**
* 第二种查询name的方式, 将可能为null的结果直接返回
*
* @return name
*/
public static String getUserName2() {
String name = "";
return name;
}
}
通过上述演示,对于方法可能返回null的情况下,通过Optional包装后
- 调用方会很容易知道此方法可能返回null,则会对返回结果进行一定的处理;
- 另外一方面使用Optional处理null的链式代码风格理论上更加简洁。
Optional中常用方法演示如下,更多Optional方法的使用参考CSDN上文章。
package com.huobi.examples;
import java.util.Optional;
/**
* 测试Optional
*
* @author hulei
* @date 2021/3/31
*/
public class OptionalTest2 {
public static void main(String[] args) {
// 包装非null的对象,如果被包装的对象是null,此方法会返回空指针异常
Optional<String> o1 = Optional.of("xxx");
// 包装可能为null的对象,被包装的对象是否为null都不会抛出异常
Optional<String> o = Optional.ofNullable(null);
// 用于测试Optional是否包装了null, 另外一种测试方式是isPresent()
Optional<Object> empty = Optional.empty();
System.out.println(empty.equals(o));
System.out.println(o.isPresent());
// 如果被包装的对象为null,则返回特定对象
String s = o.orElse("");
// 如果被包装对象为null,则通过一段代码(lambda表达式),返回特定对象
String xxx = o.orElseGet(() -> {
System.out.println("xxx");
return "";
});
// 如果被包装对象为null,则抛出异常
String s1 = o.orElseThrow(RuntimeException::new);
}
}
函数式接口
函数式接口是指仅用一个抽象方法的接口(Interface),但允许存在多个非抽象方法(Jdk1.8中允许在接口中定义非抽象方法,default方法、static方法,具体参考CSDN文章)。
按上述定义,只要接口中抽象方法仅有一个即为函数是接口,无论接口是否被@FunctionalInterface注解修饰。并且Java中很多现存接口都被纳入函数是接口的范畴:Runable、Callable等,具体参考菜鸟文章
实现函数式接口:按照实现普通接口的方式,即通过implements实现接口;通过new匿名类实现接口;JDK1.8中引入新的实现接口的方式,lambda表达式。
package com.huobi.hl;
import java.util.function.Consumer;
import java.util.stream.Stream;
/**
* 函数式接口测试
*
* @author hulei
* @date 2021/4/1
*/
public class FunctionTest {
public static void main(String[] args) {
// 通过new 匿名函数实现函数式接口
Stream.of("1", "2").forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
// 通过lambda表达式实现函数式接口
Stream.of("1", "2").forEach(System.out::println);
}
/**
* 通过implements实现函数式接口
*
* @author hulei
* @version 2021/4/1 9:45
*/
public static class MyConsumer implements Consumer<String> {
@Override
public void accept(String s) {
System.out.println(s);
}
}
}
Jdk1.8中新增的函数式接口参考菜鸟文章,以下仅介绍常用的几个:
| 函数式接口 | 方法 | 入参 | 返回 | 理解 |
|---|---|---|---|---|
| Predicate |
boolean test(T t) | T | boolean | 断言:即传入t,按照一定条件判断并返回 |
| Consumer |
void accept(T t) | T | 不返回 | 消费:即传入t,消费t,并且不返回 |
| Supplier |
T get(); | 不传入 | 返回T | 供应:即不传入,但返回值 |
| Function<T, R> | R apply(T t); | 传入T | 返回R | 应用:即传入t,消费t并按规则转换后返回 |
package com.huobi.hl;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 函数式接口测试
*
* @author hulei
* @date 2021/4/1
*/
public class FunctionTest2 {
public static <T> void main(String[] args) {
List<String> list = Stream.of("张三", "李四", "王二麻").collect(Collectors.toList());
// 使用predicate-test,判断list中是否存在长度大于2的元素
boolean match = list.stream().anyMatch(s -> s.length() >= 2);
System.out.println(match);
// 使用function-apply,获取姓名中第一个字
Object[] array = list.stream().map(s -> s.substring(0, 1)).toArray();
System.out.println(Arrays.toString(array));
// 使用consumer-accept,打印数组
list.forEach((s) -> {
System.out.print(s);
System.out.print(" ");
});
System.out.println();
// 使用supplier-get,如果user为null,通过supplier的实现提供user返回
String user = null;
String s = Optional.ofNullable(user).orElseGet(() -> "default".substring(0, 1));
System.out.println(s);
}
}
序列化分析
通过方法引用获取字段名称,下述代码摘自tk-mybatis
package tk.mybatis.mapper.weekend.reflection;
import tk.mybatis.mapper.weekend.Fn;
import java.beans.Introspector;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;
import java.util.regex.Pattern;
/**
* @author Frank
*/
public class Reflections {
private static final Pattern GET_PATTERN = Pattern.compile("^get[A-Z].*");
private static final Pattern IS_PATTERN = Pattern.compile("^is[A-Z].*");
private Reflections() {
}
public static String fnToFieldName(Fn fn) {
try {
Method method = fn.getClass().getDeclaredMethod("writeReplace");
method.setAccessible(Boolean.TRUE);
SerializedLambda serializedLambda = (SerializedLambda) method.invoke(fn);
String getter = serializedLambda.getImplMethodName();
if (GET_PATTERN.matcher(getter).matches()) {
getter = getter.substring(3);
} else if (IS_PATTERN.matcher(getter).matches()) {
getter = getter.substring(2);
}
return Introspector.decapitalize(getter);
} catch (ReflectiveOperationException e) {
throw new ReflectionOperationException(e);
}
}
}
package tk.mybatis.mapper.weekend;
import java.io.Serializable;
import java.util.function.Function;
/**
* @author Frank
*/
public interface Fn<T, R> extends Function<T, R>, Serializable {
}
编译器捕捉到可序列化的Lambda表达式,Lambda元工厂必须返回一个实现了writeReplace方法的对象;writeReplace方法需要返回一个实现readResolve方法的SerializedLambda的实现类。编译器翻译Lambda表达式的原理参考、Lambda表示式深入理解。
Lambda表达式
如函数式接口一节中所述,Lambda表达式专门用于实现函数是接口,用于取代匿名实现的复杂写法。
基本语法:(参数) -> {方法体}, 具体参考菜鸟文章。
方法引用
方法引用是一种简化的lambda表达式,使用::引用类中已经实现的方法或者构造方法,方法引用有四种类型,具体参考博客园文章,使用时需要对应特定的函数式接口。
package com.huobi.hl;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 函数式接口测试
*
* @author hulei
* @date 2021/4/1
*/
public class FunctionTest3 {
private String prop;
public String getProp() {
return prop;
}
public static <T> void main(String[] args) {
List<Integer> list = Stream.of(1, 2, 3).collect(Collectors.toList());
List<Integer> sub = Stream.of(1).collect(Collectors.toList());
// 通过实例引用非静态方法: predicate - test
boolean b = list.stream().anyMatch(sub::contains);
// 通过类引用非静态方法: function - apply, 通过类引用非静态方法、通过类引用静态方法,对应的接口函数不同
Function<FunctionTest3, String> prop = FunctionTest3::getProp;
List<String> props = new ArrayList<FunctionTest3>().stream().map(prop).collect(Collectors.toList());
// 通过类引用静态方法: consumer - accept
Stream.of("1").forEach(FunctionTest3::print);
// 引用构造方法:supplier - get
Object o = null;
Optional.ofNullable(o).orElseGet(Object::new);
}
public static void print(String s) {
System.out.println(s);
}
}
Stream API
流式API,可以用于处理集合、数组、IO等,让数据流经过管道中不同的处理方法达到最终的要求。这里介绍Steam API对集合的常用操作,Stream适合处理数据量不大的集合,对处理效率影响小,代码比较简洁
package com.huobi.hl;
import com.alibaba.fastjson.JSON;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 流式api测试
*
* @author hulei
* @date 2021/4/1
*/
public class StreamTest {
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class User {
private String id;
private String name;
private Integer age;
private String sex;
}
public static void main(String[] args) {
// 初始化数据
User zs = new User.UserBuilder().id("1").name("张三").age(18).sex("1").build();
User ls = new User.UserBuilder().id("2").name("李四").age(20).sex("1").build();
User xh = new User.UserBuilder().id("3").name("小红").age(22).sex("0").build();
// 构造集合
List<User> list = Stream.of(zs, ls, xh).collect(Collectors.toList());
// 转换对象集合: ["张三","李四","小红"]
List<String> nameList = list.stream().map(user -> user.name).collect(Collectors.toList());
System.out.println(JSON.toJSON(nameList));
// 转换为map: {"1":"张三","2":"李四","3":"小红"}
Map<String, String> collect1 = list.stream().collect(Collectors.toMap(User::getId, User::getName));
System.out.println(JSON.toJSON(collect1));
// 分组: {"0":[{"sex":"0","name":"小红","id":"3","age":22}],"1":[{"sex":"1","name":"张三","id":"1","age":18},{"sex":"1","name":"李四","id":"2","age":20}]}
Map<String, List<User>> collect2 = list.stream().collect(Collectors.groupingBy(User::getSex));
System.out.println(JSON.toJSON(collect2));
// 分组,并且转换组中对象: {"0":["小红"],"1":["张三","李四"]}
Map<String, List<String>> collect3 = list.stream().collect(Collectors.groupingBy(User::getSex, Collectors.mapping(User::getName, Collectors.toList())));
System.out.println(JSON.toJSON(collect3));
// 分组计数: {"0":1,"1":2}
Map<String, Long> collect = list.stream().collect(Collectors.groupingBy(User::getSex, Collectors.counting()));
System.out.println(JSON.toJSON(collect));
// 过滤: ["李四","小红"]
List<String> collect4 = list.stream().filter(user -> user.getAge() >= 20).map(User::getName).collect(Collectors.toList());
System.out.println(JSON.toJSON(collect4));
// 找出不满足指定集合条件的元素(双重循环): [李四, 小红]
List<String> exclude = Stream.of("张", "王").collect(Collectors.toList());
List<String> collect5 = list.stream().filter(user -> exclude.stream().noneMatch(s -> user.getName().startsWith(s))).map(User::getName).collect(Collectors.toList());
System.out.println(collect5);
}
}

浙公网安备 33010602011771号