Java 8函数式编程模式:不要使用空指针null

栏目: Java · 发布时间: 5年前

内容简介:空指针并不存在,是我们发明了它,我们现在就摆脱它,好吗?下面代码练习很简单:我们需要返回一个格式很好的行,根据他收集的点数为客户打印合适的折扣:让我们看看它返回,应该先是60,然后是10点:

空指针并不存在,是我们发明了它,我们现在就摆脱它,好吗?

下面代码练习很简单:我们需要返回一个格式很好的行,根据他收集的点数为客户打印合适的折扣:

<b>public</b> String getDiscountLine(Customer customer) {
        <b>return</b> <font>"Discount%: "</font><font> + getDiscountPercentage(customer.getMemberCard());
}
<b>private</b> Integer getDiscountPercentage(MemberCard card) {
        <b>if</b> (card.getFidelityPoints() >= 100) {
                <b>return</b> 5;
        }
        <b>if</b> (card.getFidelityPoints() >= 50) {
                <b>return</b> 3;
        }
        <b>return</b> <b>null</b>;
}
</font>

让我们看看它返回,应该先是60,然后是10点:

System.out.println(discountService.getDiscountLine(<b>new</b> Customer(<b>new</b> MemberCard(60))));
System.out.println(discountService.getDiscountLine(<b>new</b> Customer(<b>new</b> MemberCard(10))));

输出:

Discount%: 3

Discount%: null

我认为向用户显示“null”并不是您每天都想做的事情。显然,问题是我们连接了一个潜在的null 整数。修复它是很轻松的:

<b>public</b> String getDiscountLine(Customer customer) {
        Integer discount = getDiscountPercentage(customer.getMemberCard());
        <b>if</b> (discount != <b>null</b>) {
                <b>return</b> <font>"Discount%: "</font><font> + discount;
        } <b>else</b> {
                <b>return</b> </font><font>""</font><font>;
        }
}
</font>

如果没有折扣,我们返回一个空字符串。我们使用空检查污染了我们的代码。并且,更糟糕的是,我们必须通过窥视getDiscountPercentage()函数来找到我们遇到的问题,以确定它何时可以返回一个null,但是,这种技术不能在大型代码库中扩展。相反,您的API应该明确该函数可能不返回任何内容。

<b>import</b> java.util.Optional.*;
<b>public</b> String getDiscountLine(Customer customer) {
        Optional<Integer> discount = getDiscountPercentage(customer.getMemberCard());
        <b>if</b> (discount.isPresent()) {
                <b>return</b> <font>"Discount%: "</font><font> + discount.get();
        } <b>else</b> {
                <b>return</b> </font><font>""</font><font>;
        }
}
<b>private</b> Optional<Integer> getDiscountPercentage(MemberCard card) {
        <b>if</b> (card.getFidelityPoints() >= 100) {
                <b>return</b> of(5);
        }
        <b>if</b> (card.getFidelityPoints() >= 50) {
                <b>return</b> of(3);
        }
        <b>return</b> empty();
}
</font>

这里使用Optional.isPresent() 替代空检查。

但是,有时候,从我们头脑中弹出第一个想法并不总是最好的。当你玩的时候Optional,你需要反过来考虑如何使用它。每当你试图改变魔术盒中的内容时,使用.map(),应用于该魔术盒中,这样只有存在这个盒里面的东西才会转换。

<b>public</b> String getDiscountLine(Customer customer) {
<b>return</b> getDiscountPercentage(customer.getMemberCard())
      .map(d -> <font>"Discount%: "</font><font> + d).orElse(</font><font>""</font><font>);
}
</font>

代码不仅更简洁,而且一旦习惯了这种风格,它也更容易阅读。

下面测试一下没有MemberCard的Customer :

System.out.println(discountService.getDiscountLine(new Customer()));

输出:

Exception in thread "main" java.lang.NullPointerException...

我了去!我们经常匆匆忙忙地看看异常突然出现在哪里。在这里,它位于getDiscountPercentage()函数的第一行。这是由于我们从未处理过MemberCard参数为null的某些边界值,我们马上解决这个问题 - 尽快隐藏那个bug并假装我们从未见过它:

<b>private</b> Optional<Integer> getDiscountPercentage(MemberCard card) {
        <b>if</b> (card == <b>null</b>) {
                <b>return</b> empty();
        }
        <b>if</b> (card.getFidelityPoints() >= 100) {
                <b>return</b> of(5);
        }
        <b>if</b> (card.getFidelityPoints() >= 50) {
                <b>return</b> of(3);
        }
        <b>return</b> empty();
}

,我们再次错过了一个设计洞察(你看到一个模式吗?)。我们在这里快速应用了防御性编程,并针对所有其他无效数据实现保护我们的代码。但是,据说最好的防守就是进攻。如果不是在恐惧中守护我们的代码,我们会说:“等一下。因此,客户是否可以没有会员卡?如果可能存在,Customer.getMemberCard()应该返回一个Optional<MemberCard>

<b>public</b> <b>class</b> Customer {
          ...
          <b>public</b> Optional<MemberCard> getMemberCard() {
                   returnofNullable(memberCard);
          }
}

是的,我在这里触及神圣的事物。我敢改变领域实体!但是,我相信我更具表现力,因为客户和会员卡之间的关联实际上是可选的(1:0..N)

调用这段代码时我们使用.isPresent()?

<b>private</b> Optional<Integer> getDiscountPercentage(Optional<MemberCard> cardOpt) {
        <b>if</b> (!cardOpt.isPresent()) {
                returnempty();
        }
...<font><i>// Wait a bit!</i></font><font>
</font>

停!

我们一无所获!我会说这段代码变得更加丑陋!但是,有一个干净的代码规则:不要采用可以为空的参数,因为你需要做的第一件事就是检查null。在 Java 8中,不采用Optional参数作为入参。

我们看到如果有会员卡,会应用getDiscountPercentage()到MemberCard,那么我们放弃更改getDiscountPercentage(),而是改变getDiscountLine():

<b>public</b> String getDiscountLine(Customer customer) {
          <b>return</b> customer.getMemberCard()
                             .map(card -> getDiscountPercentage(card))
                             .map(d -> <font>"Discount%: "</font><font> + d)
                             .orElse(</font><font>""</font><font>);
}
</font>

输出可能让我们感到惊讶:

Discount%: Optional[3]

Discount%: Optional.empty

这是因为第4行的d不再是一个Integer类型,而是一个Optional<Integer>,如果你将鼠标悬停在第一个上.map()并查看返回类型,你会自己看到它:Optional<Optional<Integer>>。

在我们的例子中,我们使用.flatMap()而不是.map()去除额外的包装。

<b>public</b> String getDiscountLine(Customer customer) {
            <b>return</b> customer.getMemberCard()
                                    .flatMap(<b>this</b>::getDiscountPercentage)
                                    .map(d -> <font>"Discount%: "</font><font> + d)
                                    .orElse(</font><font>""</font><font>);
}
</font>

因此,只要null存在Java 8中就会给您带来问题,请不要犹豫,使用Optional 并在可能为空的魔术盒上应用转换函数。干净的代码规则变为:不要选择Optional 参数; 相反,只要你的函数想要向你的调用者发出信号,在某些情况下可能没有返回值,就返回一个Optional。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

计算统计

计算统计

Geof H.Givens、Jennifer A.Hoeting / 王兆军、刘民千、邹长亮、杨建峰 / 人民邮电出版社 / 2009-09-01 / 59.00元

随着计算机的快速发展, 数理统计中许多涉及大计算量的有效方法也得到了广泛应用与迅猛发展, 可以说, 计算统计已是统计中一个很重要的研究方向. 本书既包含一些经典的统计计算方法, 如求解非线性方程组的牛顿方法、传统的随机模拟方法等, 又全面地介绍了近些年来发展起来的某些新方法, 如模拟退火算法、基因算法、EM算法、MCMC方法、Bootstrap方法等, 并通过某些实例, 对这些方法的应用进行......一起来看看 《计算统计》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

在线进制转换器
在线进制转换器

各进制数互转换器

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具