Java中如何避免麻烦的null值判断
来源:互联网 发布:debian ubuntu 性能 编辑:程序博客网 时间:2024/05/17 07:31
null值判断以及空指针异常应该是我们在代码中经常遇到的。针对null值的处理有两种:
(1)将null值替换为null对象(本质上,是利用多态)
(2)利用Java 8 的Optional对象
首先,看下方法将null值替换为null对象如何实现?
举个栗子:
一家公用公司的系统以Site表示地点(场所),顾客的信息以Customer表示,PaymentHistory表示顾客的付款记录,BillingPlan表示账单。
重构前的代码如下:
package com.tim;/** * 通常我们代码是这么写的 * @date 2017年7月13日 */public class Site { /** * 顾客类 * @date 2017年7月13日 */ private static class Customer{ private String _name; private BillingPlan _plan; private PaymentHistory _history; public BillingPlan getPlan(){ return _plan; } public String getName(){ return _name; } public PaymentHistory getHistory(){ return _history; } } /** * 账单类 * @date 2017年7月13日 */ private static class BillingPlan{ public static BillingPlan basic(){ return new BillingPlan(); } } /** * 付款计划类 * @date 2017年7月13日 */ private static class PaymentHistory{ private int _weeksDelinquentInLastYear; public int getWeeksDelinquentInLastYear(){ return _weeksDelinquentInLastYear; } } private Customer _customer; Customer getCustomer(){ return _customer; } /** * 业务代码 * @date 2017年7月13日 */ public static void doSomething(){ Site site = new Site(); Customer customer = site.getCustomer(); BillingPlan plan; //代码某处用到 if(customer == null ){ plan = BillingPlan.basic(); }else{ plan = customer.getPlan(); } //代码某处用到 String customerName; if(customer == null){ customerName = "occupant"; }else{ customerName = customer.getName(); } //代码某处用到 int weeksDelingquent; if(customer == null || customer.getHistory() == null){ weeksDelingquent = 0; }else{ weeksDelingquent = customer.getHistory().getWeeksDelinquentInLastYear(); } System.err.println(customerName); System.err.println(weeksDelingquent); } public static void main(String[] args) { doSomething(); }}
可以看到,在业务方法代码doSomething()中存在多处判断对象是否为null,如:customer == null 或 customer.getHistory(),造成代码很冗余。
重构方法一:利用null对象替换null值,重构之后的代码如下:
package com.tim;/**重构方法一 * 利用:null对象替换null值(本质上是多态) * @date 2017年7月13日 */public class SiteV2 { /** * 顾客类空对象 * @date 2017年7月13日 */ private static class NullCustomer extends Customer{ public boolean isNull(){ return true; } public BillingPlan getPlan(){ return BillingPlan.basic(); //针对null的处理 } public String getName(){ return "occupant"; } public PaymentHistory getHistory(){ return PaymentHistory.newNull(); } } /** * 顾客类 * @date 2017年7月13日 */ private static class Customer{ private String _name; private BillingPlan _plan; private PaymentHistory _history; public BillingPlan getPlan(){ return _plan; } public String getName(){ return _name; } public PaymentHistory getHistory(){ return _history; } public boolean isNull(){ return false; } static Customer newNull(){ return new NullCustomer(); } } /** * 账单类 * @date 2017年7月13日 */ private static class BillingPlan{ public static BillingPlan basic(){ return new BillingPlan(); } } /** * 付款计划类 空对象 * @date 2017年7月13日 */ private static class NullPaymentHistory extends PaymentHistory{ public int getWeeksDelingquentInLastYear(){ return 0; } } /** * 付款计算类 * @date 2017年7月13日 */ private static class PaymentHistory{ private int _weeksDelinquentInLastYear; public int getWeeksDelinquentInLastYear(){ return _weeksDelinquentInLastYear; } static NullPaymentHistory newNull(){ return new NullPaymentHistory(); } } private Customer _customer; Customer getCustomer(){ return _customer==null?Customer.newNull():_customer; } /** * 业务代码 * @date 2017年7月13日 */ public static void doSomething(){ SiteV2 site = new SiteV2(); Customer customer = site.getCustomer(); BillingPlan plan = customer.getPlan(); String customerName = customer.getName(); int weeksDelingquent = customer.getHistory().getWeeksDelinquentInLastYear(); System.err.println(customerName); System.err.println(weeksDelingquent); } public static void main(String[] args) { doSomething(); } }
分析:
重构之后,在业务代码中对于null的处理都移到相应类的空对象中处理,因此,业务代码中只需要一行,如:BillingPlanplan =customer.getPlan();(一行代码轻松搞定)此时,无需再对customer进行null判断,因为如果customer是null时,会进行相应的null处理。
本质上,利用多态,你不必再向对象询问“你是什么类型(即使是null,我们也有相应的null对象进行相应的处理)”而后根据得到的答案调用对象的某个行为,你只管调用该行为就是了,其他的一切多态机制会为你安排妥当。
重构方法二:利用Java 8 Optional对象对null值进行重构:
package com.tim;import java.util.Optional;/**重构方法二: * 利用:Java 8 的Optional进行null值的封装 * @date 2017年7月13日 */public class SiteV3 { /** * 顾客类 * @date 2017年7月13日 */ private static class Customer{ private String _name; private BillingPlan _plan; private PaymentHistory _history; public Optional<BillingPlan> getPlan(){ return Optional.ofNullable(_plan); } public String getName(){ return _name; } public Optional<PaymentHistory> getHistory(){ return Optional.ofNullable(_history); } } /** * 账单类 * @date 2017年7月13日 */ private static class BillingPlan{ public static BillingPlan basic(){ return new BillingPlan(); } } /** * 付款计划类 * @date 2017年7月13日 */ private static class PaymentHistory{ private int _weeksDelingquentInLastYear; public int getWeeksDelingquentInLastYear(){ return _weeksDelingquentInLastYear; } } private Customer _customer; Optional<Customer> getCustomer(){ return Optional.ofNullable(_customer); } /** * 业务代码类 * @date 2017年7月13日 */ public static void doSomething(){ SiteV3 site = new SiteV3(); Optional<Customer> customer = site.getCustomer(); BillingPlan plan = customer.flatMap(c->c.getPlan()).orElse(BillingPlan.basic()); String customerName = customer.map(c->c.getName()).orElse("occupant"); int weeksDelingquent = customer.flatMap(c->c.getHistory()).map(h->h.getWeeksDelingquentInLastYear()).orElse(0); System.err.println(customerName); System.err.println(weeksDelingquent); } public static void main(String[] args) { doSomething(); }}
分析:
利用Java 8的Optional对象重构相比重构方法一中利用null对象替换null值,代码长度更短,由于对于空情况的判断只通过Optional来实现,无需重构方法一中重新编写null对象,最后在调用值的时候也只是流式处理,也只需要一行代码就可获取所需的值,无需做null判断。
补充:关于Java 8中的Optional对null的处理,参考书籍:《Java 8 实战》P203-P219
- Java中如何避免麻烦的null值判断
- null值判断的一个避免错误
- 在Java代码中避免 "!=null " 判断
- java 中null的判断
- 在Java中如何避免“!=null”式的判空语句?
- c#中如何对NULL值更好的判断
- java 如何避免“!=null”式的判空语句?
- Java中对null或空值的判断处理
- Java中null或空值的判断处理
- Velocity中如何判断null
- Velocity中如何判断null
- Velocity中如何判断null
- Js 中如何判断Null
- Velocity中如何判断null
- JS中如何判断null
- JS中如何判断null
- JS中如何判断null
- 在Java中避免“!= null”语句?
- 统一的异常处理
- 萨达时代科技哈萨克打扫打扫
- 大型网站架构(四)随需应变:网站的可扩展架构
- JS练习题(1)
- static
- Java中如何避免麻烦的null值判断
- Swift 创建控制器时自带xib加载在iOS8系统崩溃的问题
- 插入排序(Insertion Sort)
- [JZOJ4426]. 【HNOI2016模拟4.4】Stage
- JDBC事务的隔离级别
- tp5中调用一些类的静态方法前面加\
- UVA 10491 Cows and Cars
- Java Web学习路线
- TouchEvent分发机制