前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用“空”对象替代引用是否为空判断

使用“空”对象替代引用是否为空判断

作者头像
用户1608022
发布2018-04-11 14:40:49
7.5K0
发布2018-04-11 14:40:49
举报

使用Null对象替代引用是否为空判断

编程语言中最常见运行时异常非NullPointerException莫属,只要程序依赖于外部的输入数据,比如说http请求传递的查询字符串参数、关系数据库连接、磁盘文件读取,空引用异常就无法避免。通常,程序需要满足某些条件才能正常的往下执行,假如这些条件依赖外部输入数据,而这些外部输入的数据肯定无法保证百分百不出错,比如说网络连接失败、数据库用户名密码错误等,当程序被这些节外生枝的障碍打断时,空引用异常就极有可能被引发。

比如说,原本我们调用一个方法,这个方法会执行连接数据库操作并返回一个数据库连接对象。然而,由于某种原因导致连接失败,这个方法并没有照常返回数据库连接对象而是返回一个null值,当我们使用对象时假如不进行是否为空检测,程序就会抛出NullPointerException,但是假如进行检测的话代码又会变得极其丑陋,没有程序员喜欢自己的代码中到处都是if(connection == null)这样的条件判断。

而且这种对象是否为空的判断还会传播,在一系列函数调用的过程中,其中某一个调用返回一个null值, 这个函数调用栈中所有的调用都有可能受到波及,直到最外层的调用。这些函数中会出现很多是否为空的判断,严重影响代码的美观程度、可读性,甚至还增加了出BUG的几率。

空引用问题是永远无法避免的, 除非从语言层面进行解决, 现在一些现代的新语言的设计已经引入避免此问题的机制。但是一些年纪较大的语言, 比如说Java,只能通过一些代码编写技巧来尽量弱化空引用带来的问题。「使用Null对象代替是否为空判断」是一种流行的解决此问题的技巧。

public class Main {
    public static void main(String[] args) {
        getWorstLanguage(false).toString();
        getBestLanguage(false).toString();
    }
    
    public  static String getWorstLanguage( boolean exists) {
        if(exists ) {
            return "Java";
        } else {
            return "";
        }
    }
    
    public static String getBestLanguage( boolean exists ) {
        if( exists ) {
            return "PHP";
        } else {
            return null;
        }
    }
}

main方法中第二行代码执行会抛出空引用异常。 其实两个方法的if条件都没有被满足,然而它们一个返回长度为0的空字符串,一个返回null, 空字符串虽然没有实际意义,但却并非是空引用, 因此在其上执行操作不会抛出空引用异常,代码也是相对安全的。

想要避免getBestLanguage方法使用时有可能会引发的异常,一般都会这么做

        String  result = getBestLanguage(false);
        if (result != null) {
            result.toString();
        }

显然这并不如getWorstLanguage方法返回空字符串的方式来的优雅。如果你对字符串的长度有依赖,可以以

if (result.length() == 0 ) {}

这种方式进行判断,至少它没有出现异常的危险。

同样,在方法返回值为其它对象类型的时候也可以借鉴并扩展这种思路。

public class Customer {
        String _name;
 
        public Customer() { }
 
        public Customer(String name) {
            _name = name;
        }
 
        public String GetName() {
            return _name;
        }
    }
 
    public class Site {
        Customer _customer;
 
        public Site() {
        }
 
        public Site(Customer customer) {
            _customer = customer;
        }
 
        public Customer GetCustomer() {
            return _customer;
        }
    }

有Customer 和 Site  两个类,Site类的GetCustomer会返回一个Customer对象,但假如实例化Site对象时使用无参数构造函数,GetCustomer将返回一个空引用。

        Site site = new Site();
        Customer customer = site.GetCustomer();
        String name = customer == null ? "guest" : customer.GetName();
        System.out.println(name);

像这种方式使用那两个类,在调用GetName方法时, 除非进行是否为空校验,否则程序会抛出空引用异常。 假如site对象和它的GetCustomer会被频繁的调用,将是难以忍受的,因为customer == null 这样的条件判断会充斥在项目代码的各处。

我们可以引入一个“空”对象来改善这个问题

 public class Customer {
        String _name;
 
        public Customer() { }
 
        public Customer(String name) {
            _name = name;
        }
 
        public String GetName() {
            return _name;
        }
 
 
        public boolean IsNull()
        {
            return false;
        }
    }
 
    public class NullCustomer extends Customer {
        public NullCustomer() {}
 
        @Override
        public boolean IsNull() {
            return true;
        }
 
        @Override
        public String GetName() {
            return "guest";
        }
    }
 
    public static class Site {
        Customer _customer;
 
        public Site() {
        }
 
        public Site(Customer customer) {
            _customer = customer;
        }
 
        public Customer GetCustomer() {
            return _customer == null ? new NullCustomer(): _customer;
        }
    }

Customer类添加了一个IsNull的实例方法。

Site 类的GetCustomer方法内部进行了_customer 成员是否为空的判断,这其实就是把原来在外面的空引用判断提取到了类的内部,把逻辑给封装了起来。

与此同时, 我们引入了NullCustomer类型,它继承至Customer,是一个Customer的特例,表示Site对象中_customer成员为空的情况,替代它非空时的行为, 这正如他的命名NullCustomer。

原本调用Site对象GetCustomer有可能返回的null值被NullCustomer类的实例所代替, 这样代码的外部可以放心的使用GetCustomer的返回值,不用再提心吊胆的生怕返回空值,也不用做是否为空的判断。

public static void main(String[] args) {
        Site site = new Site();
        Customer customer = site.GetCustomer();
        String name = customer.GetName();
        System.out.println(name);
    }

如果要确定GetCustomer的返回值是否为空的情况,可用调用customer对象的IsNull方法

        Customer customer = site.GetCustomer();
        if(customer.IsNull()) {
            
        }

因为不管是Customer 类还是NullCustomer都实现了这个方法, 并且Customer的IsNull方法返回的false, NullCustomer类重写了重Customer继承来的IsNull方法,并返回为true,这种利用多态来面向接口编程的方式,正好满足了我们的需求。

总而言之,引用“空”对象可以很好的解决空引用这个牛皮癣似的问题。然而,引入这个机制还需要跟代码的实际情况结合,假如某个对象为空的情况只出现有限的几次,那引入这种机制显得有些杀鸡用牛刀的味道了,使用是否为空判断反而更加轻松;当某个对象是否为空的判断频繁的出现在代码之中, 那么使用“空”对象来代替if判断才有实际的意义。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-02-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 带你撸出一手好代码 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档