简介
java8函数式编程中optional的简单使用,函数式接口的了解,并行流的使用。
optional
可以更优雅的来避免空指针异常。类似于包装类,把具体的数据封装到optional对象内部,然后使用optional的方法去操作封装好的数据。
创建optional对象
ofNullable方法
使用静态方法ofNullable把数据封装成optional对象,方法的参数可为空。
通常会用在方法上,把方法的返回值封装成optional对象返回。
public class OptionalTest01 {
public static void main(String[] args) {
Optional<Integer> num = getNum();
num.ifPresent(num1-> System.out.println(num1));
}
public static Optional<Integer> getNum(){
Integer a= 2;
return Optional.ofNullable(a);
}
}
当a=null时,则不会输出任何东西,但也不会报空指针异常。
扩展:
mybatis3.5以上是支持optional类型的,可以直接把dao层的返回值定义为optional,mybatis会自动的把数据封装成optional对象返回。
of方法
使用optional的静态方法of来封装optional对象,参数不能为null,否则会报空指针异常。
package com;
import java.util.Optional;
public class OptionalTest02 {
public static void main(String[] args) {
Optional<Integer> num = getNum();
num.ifPresent(num1-> System.out.println(num1));
}
public static Optional<Integer> getNum(){
Integer a= null;
return Optional.of(a);
}
}
empty方法
empty可以把null封装成optional对象,可以和of方法一起使用。
package com;
import java.util.Optional;
public class OptionalTest03 {
public static void main(String[] args) {
Optional<Integer> num = getNum();
num.ifPresent(num1-> System.out.println(num1));
}
public static Optional<Integer> getNum(){
Integer a= null;
return a==null?Optional.empty():Optional.of(a);
}
}
所以,还是ofNullable方法用的比较多,empty和of方法比较少用。
判断
ifPresent
会判断封装的数据是否为空,不为空时才会执行具体的操作,可以安全的消费值
isPresent
会判断封装的数据是否为空,为空返回false,不为空返回true。
package com;
import java.util.Optional;
public class OptionalTest07 {
public static void main(String[] args) {
Optional<Integer> num = getNum();
if(num.isPresent()){
System.out.println(num.get());
}
else
{
System.out.println("value is null");
}
}
public static Optional<Integer> getNum(){
Integer a= null;
return Optional.ofNullable(a);
}
}
安全的获取值
orElseGet
安全的获取值,如果optional对象没有值,则返回设置的值,如果有值,则获得值。
package com;
import java.util.Optional;
public class OptionalTest04 {
public static void main(String[] args) {
Optional<Integer> num = getNum();
Integer integer = num.orElseGet(() -> 2);
System.out.println(integer);
}
public static Optional<Integer> getNum(){
Integer a= null;
return Optional.ofNullable(a);
}
}
orElseThrow
获取数据的时候,如果值为空,那么会抛出指定的异常。方便springboot的统一异常的处理。
package com;
import java.util.Optional;
public class OptionalTest05 {
public static void main(String[] args) {
Optional<Integer> num = getNum();
try {
Integer integer = num.orElseThrow(() -> new RuntimeException("value is null"));
System.out.println(integer);
}catch (Throwable e){
e.printStackTrace();
}
}
public static Optional<Integer> getNum(){
Integer a= null;
return Optional.ofNullable(a);
}
}
过滤
可以使用filter方法进行过滤,如果都不符合过滤条件,optional还是会变成一个null的对象。
package com;
import java.util.Optional;
public class OptionalTest06 {
public static void main(String[] args) {
Optional<Integer> num = getNum();
Optional<Integer> integer = num.filter(a -> a > 5);
integer.ifPresent(b-> System.out.println(b));
}
public static Optional<Integer> getNum(){
Integer a= 2;
return Optional.ofNullable(a);
}
}
函数式接口
只有一个抽象方法的接口就是函数式接口,函数式接口可以用注解@FunctionalInterface修饰,也可以没有这个注解
常见的函数式接口
- consumer:消费接口,可以对传入方法的参数做操作,没有返回值
- function:计算转换接口,可以对传入方法的参数做计算或转换,有返回值
- predictate:判断接口,可以对传入方法的参数条件判断,返回boolean
- supplier:可以在方法中创建对象并返回。
方法引用
使用lambada表达式的时候,如果方法体只有一个方法的调用,可以使用方法引用进一步简化代码。
这个其实是作为了解的,没有必要为了简化而简化。遇到这种写法的时候知道就好了
格式
引用类的静态方法时:类名:: 方法名
引用对象的实例方法时:对象名:: 方法名
构造器引用:类名::new
num.ifPresent(num1-> System.out.println(num1));
可以改成num.ifPresent(System.out::println);
在lambada式上,alt+回车,如果有图中的选项,则可以使用方法引用的方式简化代码。
stream流的优化
基本数据类型优化
很多的stream流中的方法,都是使用了泛型的,所以涉及到的参数和返回值都是引用数据类型。在方法中,会有很多的装箱拆箱的操作,会损耗性能,比如authors.stream().map(author -> author.getAge());
这个代码中,传入的参数a是Integer的,在比较的时候,会拆箱为int,然后结果又要装箱成integer。
常用的优化方法:
- mapToInt
- mapToLong
- mapToDouble
- flatToDouble
- flatToInt
并行流
stream流中有大量的数据时,可以使用并行流去提高效率,它会把任务分配给多个线程去操作。它不需要我们手动去实现复杂的多线程编程,而是直接调用parallel方法或者通过parallelStream直接获取并行流对象
parallel方法
package com;
import java.util.stream.Stream;
public class StreamTest6 {
public static void main(String[] args) {
Integer[] arr = {1,1,2,2,3,3,4,4,5,6};
Stream.of(arr)
.parallel()
.peek(n-> System.out.println(n+Thread.currentThread().getName()))
.distinct()
.forEach(arr3-> System.out.println(arr3));
}
}
解析:
这个peek方法是stream流中的调试线程方法
如果不是用parallel方法,输出结果是:
可以看到是只有一个线程的。
parallelStream方法
package com;
import java.util.ArrayList;
import java.util.List;
public class StreamTest4 {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.parallelStream()
.peek(n-> System.out.println(n+Thread.currentThread().getName()))
.forEach(i-> System.out.println(i));
}
}