Java8 Optional使用指南:返回猫还是返回装了猫的盒子?

今天在Youtube上看Optional用法时发现Coding with John的教程很不错,浅显易懂,跟大家分享一下。整理不易,大伙儿觉得写的不错的,点赞、收藏支持下,谢谢。

资料来源:https://www.youtube.com/watch?v=vKVzRbsMnTQ


场景: 通过猫的姓名找到猫对象,最终输出其年龄

public class OptionalTutorial {
    public static void main(String[] args) {
        Cat myCat = findCatbyName("Fred");
        // 目标:输出目标猫的年龄
        System.out.println(myCat.getAge());
    }
    // 通过姓名获取猫对象
    private static Cat findCatbyName(String name) {
        Cat cat = new Cat(name,3);
        return cat;
    }
}

// 自定义猫对象
@Data
public class Cat {
    private String name;
    private Integer age;

    public Cat(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
}

但这个过程并非高枕无忧,在findCatbyName方法里,我们一般会通过前往数据库查找,如果库里没有数据,方法返回空,main方法里的myCat将为null,于是myCat.getAge()报空指针。所以,一般我们会这么处理:

public class OptionalTutorial {
    public static void main(String[] args) {
        Cat myCat = findCatbyName("Fred");
        // 目标:输出目标猫的年龄
        if (myCat != null) {
             System.out.println(myCat.getAge());
        } else {
            // 目标猫咪不存在,输出一个默认值0
            System.out.println(0);
        }
    }
    // 通过姓名获取猫对象
    private static Cat findCatbyName(String name) {
        // 模拟从数据库获取名为name的猫咪
        Cat cat = new Cat(name,3);
        return cat;
    }
}

上面的if语句看着很冗余,这也是Optional想要解决的痛点——提醒对象的使用者,这个对象可能为空,你要注意处理为空的情况,免得报空指针了!

“Optional就像一个盒子,如果你放入其中的cat实例确实存在,那盒子里放的就是它;但如果程序没有找到这只cat(即为null),那盒子里就空空如也,但Optional这个盒子还是会返给你。Optional这个类存在的目的,就是提醒使用它的人,返回泛型对象可能为null,要注意留心处理。”

如何创建一个’盒子’呢?

Cat cat = new Cat("Fred",3);

// ofNullable静态方法,见字明义 —— ‘盒子’可能为空,所以cat如果为空,它也能接受
Optional<Cat> optionalCat1 = Optional.ofNullable(cat);

// of静态方法,‘盒子’如果是空的则会报NPE
Optional<Cat> optionalCat2 = Optional.of(cat); 

经过一顿操作猛如虎,原来的代码可以改造为:

public class OptionalTutorial {
    public static void main(String[] args) {
        Optional<Cat> myOptionalCat = findCatbyName("Fred");
        // 目标:输出目标猫的年龄
        if (myOptionalCat.isPresent()) { // isPrent()方法 -> “盒子里有东西吗?”
            System.out.println(myOptionalCat.get().getAge()); //有则获取盒子里的东西,然后调它的getAge方法
        } else {
            System.out.println(0);
        }
    }
    // 通过姓名获取猫对象,不同地是,我们返回一个‘可能装了猫咪的盒子’,而不是猫咪本身
    private static Optional<Cat> findCatbyName(String name) {
        Cat cat = new Cat(name,3);
        return Optional.ofNullable(cat);
    }
}

看完上面的代码,有些友友可能要抓狂了——你在逗我吗?这和没改造前的代码相比,也没便捷哪里去,甚至更不复杂了好吗?大家稍安勿躁,Optional还有一些有用的方法,比如最常用的orElse(); 它也不难理解——“盒子有目标猫咪就给我目标猫咪,如果没有,就用这个入参替代”:

public class OptionalTutorial {
    public static void main(String[] args) {
        Optional<Cat> myOptionalCat = findCatbyName("Fred");
        // 从盒子里取猫 —— 盒子不为空则取之,为空则代之以orElse的入参
        Cat cat = myOptionalCat.orElse(new Cat("default cat",0));
        // 目标:输出目标猫的年龄
        System.out.println(cat.getAge());
    }
    
    // 通过姓名获取猫对象,不同地是,我们返回一个‘可能装了猫咪的盒子’,而不是猫咪本身
    private static Optional<Cat> findCatbyName(String name) {
        Cat cat = new Cat(name,3);
        return Optional.ofNullable(cat);
    }
}

经过上面这一步,代码是不是清爽了一些?其实还有更简洁的方法,比如我们最终要获取的是猫的年龄,可以直接对‘可能装着猫的盒子’进行映射,和StreamAPI里的map方法用起来差不多:

public class OptionalTutorial {
    public static void main(String[] args) {
        Optional<Cat> myOptionalCat = findCatbyName("Fred");
        // 目标:输出目标猫的年龄
        System.out.println(
            myOptionalCat.map(Cat::getAge) // 盒子里本来放着猫, 现在映射成猫的年龄
                           .orElse(0) // 没有猫或者猫的年龄为null? —— 直接打印默认值0
        ); //此时输出0
    }
    
    // 通过姓名获取猫对象,不同地是,我们返回一个‘可能装了猫咪的盒子’,而不是猫咪本身
    private static Optional<Cat> findCatbyName(String name) {
        Cat cat = new Cat(name, null);
        return Optional.ofNullable(cat);
    }
}

补充: 其实,除orElse()以外,Optional这个‘盒子’还提供了比如orElseThrow()方法:在盒子为空时抛出异常,但需要手动try catch捕捉‘抛出’的throwable。这个方法,笔者感觉目的在于可以定制抛出的错误类型,比如找不到Fred猫?抛一个自定义的、明确的“猫找不到”异常,而不是直接甩个NPE。

public class OptionalTutorial {
    public static void main(String[] args) {
            Optional<Cat> myOptionalCat = findCatbyName("Fred");
            try {
                Cat myCat = myOptionalCat.orElseThrow(
                        () -> {
                            throw new RuntimeException("Can't find THE cat!");
                        }
                );
                System.out.println(myCat.getAge());
            } catch (Throwable t) { // 必须手动捕捉‘throw’并打印栈信息
                t.printStackTrace();
            }
        }

    // 通过姓名获取猫对象,不同地是,我们返回一个‘可能装了猫咪的盒子’,而不是猫咪本身
    private static Optional<Cat> findCatbyName(String name) {
        Cat cat = new Cat(name,3);
        return Optional.ofNullable(cat);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值