动手学习_动手选择值

动手学习

由于冠状病毒的存在,可选的东西在空中,一切都变得可选,例如可选的公共聚会,可选的在家工作,可选的旅行等。

我现在是时候谈论处理NULL引用的软件工程中真正的“ 可选 ”了。

托尼·霍尔(Tony Hoare)坦言,他发明了空(Null)犯了数十亿美元的错误。 如果您还没有看过他的演讲,那么我建议您看一下Null-References-The-Billion-Dollar-Mistake

我将与null分享一些反模式 ,以及如何使用Optional或MayBe这样的抽象方法解决它。

在此示例中,我们将使用可以包含一些空值的简单值对象。

public class Person {
    final String firstName;
    final String lastName;
    
     final String email; // This can be null
    final String phone; //This can be null
}

该值对象的电子邮件和电话号码可以为空值。

方案:电子邮件和电话号码上的联系人

不使用可选

第一次尝试将基于检查null,如下所示

//Not using optional
        if (p.email != null) {
            System.out.println("Sending email to " + p.email);
        }

        if (p.phone != null) {
            System.out.println("Calling " + p.phone);
        }

这是多年来所做的。 具有收集结果的另一种常见模式。

List<Person> p = searchPersonById("100");

        if (p.isEmpty()) {
            System.out.println("No result");
        } else {
            System.out.println("Person" + p.get(0));
        }

以错误的方式使用可选

Optional<String> phone = contactNumber(p);
        Optional<String> email = email(p);

        if (phone.isPresent()) {
            System.out.println("Calling Phone " + phone.get());
        }
        if (email.isPresent()) {
            System.out.println("Sending Email " + email.get());
        }

这样做好一点,但是通过在代码中添加if / else块,将Optional的所有优点都抛弃了。

永远快乐可选

//Always Happy
        Optional<String> phone = contactNumber(p);
        Optional<String> email = email(p);

        System.out.println("Calling Phone " + phone.get());
        System.out.println("Sending Email " + email.get());

很高兴感到高兴,但是当您尝试使用Optional时,您所做的假设很大,或者您不需要Optional。

嵌套属性可选

对于这种情况,我们将扩展Person对象并添加Home属性。 并非每个人都可以拥有房屋,因此最好不要使用该房屋。 让我们看看在这种情况下联系人场景如何工作

//Nested Property
        if (p.getHome() != null) {
            System.out.println("Sending Postal mail " + p.getHome().address);
        }


        if (p.getHome() != null && p.getHome().getInsurance() != null) {
            System.out.println("Sending Notification to insurance " + p.getHome().getInsurance().getAgency());
        }

在这里,代码将具有大量嵌套的空检查变得越来越糟。

基于优先级的默认

在这种情况下,我们首先尝试通过家庭住址与他人联系,如果该人不可用,则请通过办公地点与他人联系。

//Address has priority , first home and then Office

        if (p.home != null) {
            System.out.println("Contacted at home address " + p.home.address);
            return; // Magical return for early exit
        }

        if (p.office != null) {
            System.out.println("Contacted at office address " + p.office.address);
            return; // Magical return for early exit
        }

这种类型的场景需要使用提前控制流来尽早返回,并使代码难以理解和维护。

这些是一些常见模式,其中未使用可选选项或使用了错误的方式。

可选使用方式

让我们看看一些使用可选的好方法。

根据领域知识使属性为可选

使属性成为可选属性非常容易。

public Optional<String> getEmail() {
        return Optional.ofNullable(email);
    }

    public Optional<String> getPhone() {
        return Optional.ofNullable(phone);
    }

是的,允许将其设为“可选”,没有人会为此而绞尽脑汁,并且可以毫无恐惧地随意这样做。 更改完成后,我们可以编写如下内容

//Use Optional
        p.getEmail().ifPresent(email -> System.out.println("Sending email to " + email));
        p.getPhone().ifPresent(phone -> System.out.println("Calling " + phone));

//Optional for Collection or Search type of request
 Optional
   
   
    
    

It looks neat, first step to code without explicit if else on application layer.

Use some power of Optional

//Use IfPresent & other cool things
        phone
                .filter(number -> hasOptIn(number))
                .ifPresent(number -> System.out.println("Calling Phone " + number));

        email
                .filter(m -> hasOptIn(m))
                .ifPresent(m -> System.out.println("Sending Email " + m));

Optional is just like stream, we get all functional map,filter etc support. In above example we are checking for OptIn before contacting.

Always happy optional

Always happy optional that calls "get" without check will cause runtime error on sunday midnight, so it advised to use ifPresent

//Don't do this
        System.out.println("Calling Phone " + phone.get());
        System.out.println("Sending Email " + email.get());

        //Use ifPresent to avoid runtime error
        phone.ifPresent(contact -> System.out.println("Sending email to " + contact));
        email.ifPresent(contact -> System.out.println("Calling " + contact));

Nested Optional

p.getHome().ifPresent(a -> System.out.println("Sending Postal mail " + a.address));

    p.getHome()
                .flatMap(Person.Home::getInsurance)
                .ifPresent(a -> System.out.println("Sending Notification to insurance " + a.agency));

Flatmap does the magic and handles null check for home and convert  insurance object also.

Priority based default

//Address has priority , first home and then Office

Optional<String> address = Stream
                .of(person.getHome().map(Home::getAddress), person.getOffice().map(Office::getAddress))
                .filter(Optional::isPresent)
                .map(Optional::get)
                .findFirst();

        address
                .ifPresent(add -> System.out.println("Contacting at address " + add));

This example is taking both home & office address and pick the first one that has value for sending notification. This particular pattern avoids lots of nested loops.

Else branch

Optional has lots of ways to handle else part of the scenario like returning some default value(orElse) , lazy default value (orElseGet) or throw exception(orElseThrow).

What is not good about optional

Each design choice has some trade off and optional also has some. It is important to know what are those so that you can make careful decision.

Memory indirection

As optional is container , so every access to value need extra jump to get real value. Optional is not good choice for element in array or collection.

No serialization

I think this is good decision by Jdk team that does not encourage people to make instance variable optional. You can wrap instance variable to Optional at runtime or when required for processing.

翻译自: https://www.javacodegeeks.com/2020/03/hands-on-optional-value.html

动手学习

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值