【Java8新特性】3.Optional类

从Java8开始,在java.util包下新增了Optional类、及对应基本类型的OptionalDouble类、OptionalInt类、OptionalLong类,引入这些类的目的是为了更好的解决令人头疼的空指针异常问题。Optional类可能包含或不包含非空值的容器对象,如果值存在则调用isPresent()方法会返回true,调用get()方法会返回该对象,正是因Optional提供很多有用的方法,这样我们就不用显式进行空值检测了。

用demo演示下Optional容器的相关API用法(先定义一个Milk类):

/** 
 * Java8 新的容器类Optional Demo  Milk实体类
 *  
 * @createTime 2020/10/13 16:59
 * @author weixiangxiang
 */
class Milk implements Serializable{
    public String milkId;
    public String milkName;
    public String milkNbr;
    public Double milkPrice;

    public Milk() {}

    public Milk(String milkId, String milkName, String milkNbr, Double milkPrice) {
        this.milkId = milkId;
        this.milkName = milkName;
        this.milkNbr = milkNbr;
        this.milkPrice = milkPrice;
    }

    /** get/set省略... */

    @Override
    public String toString() {
        return "Milk{" +
                "milkId='" + milkId + '\'' +
                ", milkName='" + milkName + '\'' +
                ", milkNbr='" + milkNbr + '\'' +
                ", milkPice=" + milkPrice +
                '}';
    }
}

1、创建Optional的实例

有三种创建方式:empty()、of()、ofNullable(),均为静态方法。

public class XxxTest {
    /** 日志记录器 */
    private static Logger logger = LoggerFactory.getLogger(XxxTest.class);

    public static void main(String[] args) {
        List<Milk> milkList = new ArrayList<>();
        Milk milk1 = new Milk("1", "蒙牛纯牛奶", "mn10001", 4.2);
        Milk milk2 = new Milk("2", "蒙牛儿童奶", "mn10002", 6.0);
        Milk milk3 = null;
        milkList.add(milk1);
        // 返回一个空的 Optional实例
        Optional op1 = Optional.empty();
        // 返回具有 Optional的当前非空值的Optional
        Optional<Milk> op2 = Optional.of(milk2);
        // 返回一个 Optional指定值的Optional,如果非空,则返回一个空的 Optional 
        Optional<Milk> op3 = Optional.ofNullable(milk3);
    }
}

2、取值、判断、默认

Optional类的取值方法get()、OptionalDouble类的取值方法getAsDouble()、OptionalInt类的取值方法getAsInt()、OptionalILong类的取值方法getAsLong(),当Optional容器有值,则返回值,反之则抛出NoSuchElementException异常。

为了防止异常,取值前应先进行值存在判断:isPresent() 或 ifPresent(Consumer<? super T> consumer) 或ifPresent(DoubleConsumer consumer) 或ifPresent(IntConsumer consumer) 或ifPresent(LongConsumer consumer)。isPresent() 返回true表示Optional容器内存在值,ifPresent则让指定的消费者Consumer去接收值(存在值去处理,不存在值不做任何处理)。

当然获取值为null时,也可以指定默认结果:orElse(默认值)、orElseGet(Supplier other) 或orElseGet(DoubleSupplier other) 或orElseGet(IntSupplier other) 或orElseGet(LongSupplier other)、orElseThrow(Supplier<X> exceptionSupplier) 。orElse()直接指定默认值,orElseGet()调用指定的支持者Supplier处理并返回其结果,orElseThrow()指定抛出的异常(这个方法是Java10新增的)。

public class XxxTest {
    /** 日志记录器 */
    private static Logger logger = LoggerFactory.getLogger(XxxTest.class);

    public static void main(String[] args) {
        // op3出现异常:NoSuchElementException
//        logger.info("op2:" + op2.get() + ",op3:" + op3.get()); 
        // 不存在值时,赋默认值null
        logger.info("op2:" + op2.get() + ",op3:" + op3.orElse(null)); 
        // 不存在值时,赋其他对象
        logger.info("op2:" + op2.get() + ",op3:" + op3.orElseGet(Milk::new)); 
        // 不存在值时,抛指定异常
        logger.info("op2:" + op2.get() + ",op3:" + op3.orElseThrow(NullPointerException::new)); 
    }
}

3、与Stream流相关的API

Optional类有三个关联的API:filter()、map()、flatMap(),OptionalDouble类、OptionalInt类、OptionalLong类则不存在!!!

  • public Optional filter(Predicate predicate):根据设置的条件进行过滤操作。
  • public <U> Optional<U> map(Function<? super T,? extends U> mapper):进行映射操作,元素到元素。
  • public <U> Optional<U> flatMap(Function<? super T,Optional<U>> mapper):进行映射操作,元素到流。
public class XxxTest {
    /** 日志记录器 */
    private static Logger logger = LoggerFactory.getLogger(XxxTest.class);

    public static void main(String[] args) {
        List<Milk> milkList = new ArrayList<>();
        Milk milk1 = new Milk("1", "蒙牛纯牛奶", "mn10001", 4.2);
        Milk milk2 = new Milk("2", "伊利纯牛奶", "mn10002", 4.5);
        Milk milk3 = new Milk("3", "蒙牛儿童奶", "mn10003", 5.0);
        Milk milk4 = new Milk("4", "蒙牛酸奶", "mn10004", 3.5);
        milkList.add(milk1);
        milkList.add(milk2);
        milkList.add(milk3);
        milkList.add(milk4);

        // 过滤操作:将milkList的milkName字段中含有"纯牛奶"的对象过滤出来
        Optional<Milk> filterMilk = milkList.stream().filter(milk -> milk.getMilkName().contains("纯牛奶")).findAny();
        if (filterMilk.isPresent()) {
            logger.info(filterMilk.get().toString()); // Milk{milkId='1', milkName='蒙牛纯牛奶', milkNbr='mn10001', milkPice=4.2}
        }
        // 映射操作(元素 -> 元素):将milkList的milkPrice字段都扩大2倍后输出
        Optional<Double> mapOptional = milkList.stream().map(milk -> milk.getMilkPrice() * 2).findFirst();
        if (mapOptional.isPresent()) {
            logger.info(mapOptional.get().toString()); // 8.4
        }
        // 映射操作(元素 -> 流):将milkList的milkNbr字段都追加字符串p后输出
        Optional<StringBuffer> flatMapMilk = milkList.stream().flatMap(milk -> Stream.of(new StringBuffer(milk.getMilkNbr()).append("p"))).findFirst();
        flatMapMilk.ifPresent(m ->logger.info(m.toString())); //mn10001p
    }
}

注意的问题:

过滤操作中满足条件的对象有2个,结果却只能操作其中一个对象,原因是示例中的List集合在使用stream进行过滤,映射等操作后,直接findXxx()获取了Optional对象,这里并没有进行终端完结操作(如用.collect()方法、foreach()方法等结尾),所以只能对List集合中符合条件的第一个对象进行中间操作了!!!

4、其他

Optional类及OptionalDouble类、OptionalInt类、OptionalLong类都重写了equals()、hashCode()、toString()等方法。

小结一下:

至此,参考Optional类的API进行了上述分类和总结,可以看到Optional类极大方便了开发中对NPE问题的控制,实用而不失优雅的解决了空指针异常。不积硅步无以至千里,点滴付出终将有所收获,共同进步 ~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值