第四章——可选类型(哨兵值)

很多情况下,一个操作可能返回一个值,也可能不返回值。比如在读取到文件的末尾时,你会希望没有返回值:

int ch;
while ((ch = getchar()) != EOF) {
printf("Read character %c\n", ch);
}
printf("Reached end-of-file\n");
复制代码

EOF通过#define被预定义为-1。所以当文件中还有内容时,getchar会返回内容,如果访问到了文件末尾,getchar就会返回-1。

有时候没有返回值也表示“未找到”。比如这段C++的代码:

auto vec = {1,2,3}
auto iterator = std::find(vec.begin(), vec.end(), someValue);
if (iterator != vec.end()) {
std::cout << "vec contains " << *iterator << std::endl;
}
复制代码

你可以用一个迭代器与vec.end()比较,如果相等表示已经迭代到了容器的末端。但你一定不会用它来获取某个值。find方法用vec.end()表示容器中没有这个值。

还有些时候,我们根本无法获得返回值,因为在函数执行的过程中发生了比较严重的错误。我们都知道空指针的例子,这段很普通的Java代码就很有可能抛出一个NullPointerException异常:

int i = Integer.getInteger("123");
复制代码

在这段代码中我们理解错了getInteger()方法的用法[1],导致它返回了null。当null被转换成int类型时,Java抛出了NullPointerException异常。

再看一个OC中的例子:

[[NSString alloc] initWithContentsOfURL:url
encoding:NSUTF8StringEncoding
error:&error];
复制代码

这时的NSString可能为nil,于是我们需要检查(也只有此时需要检查)error指针。如果NSString不为nilerror指针也就不一定有效了。

在上面这些例子中,函数返回了一个“神奇”值来表示它其实没有返回真正有用的值。这样的“神奇”值被成为“哨兵值(sentinel value)”。

但这种解决方案可能会带来一些问题。返回的结果看上去像是一个真正的值。比如-1也是一个有效的整数而你不会把它打印出来。v.end()也是迭代器但你如果试图使用它,你无法确定会得到什么值。而且每个人都希望在Java抛出NullPointerException异常时能够打印当前的调用堆栈。

所以哨兵值很容易导致错误。你有可能忘记了检查它,而是错误的使用了一个哨兵值。他们还要求实现对问题有一定了解,比如C++的end迭代器。很多时候你需要去查一查文档才能确定怎么使用。而且我们没有办法让函数告诉调用者它不能失败。比如调用一个函数返回的是指针,那这个指针就不能为nil。但一般不通过查阅文档,我们无法区分,何况有些时候文档有可能会出错。

在OC中,向nil对象发送消息是安全的。如果根据方法签名返回的是对象,那么nil对象的方法会返回nil,如果方法签名返回的是结构体,那么nil对象的方法会返回结构体的零值。但是,考虑下面这段代码:

NSString *someString = ...;
if ([someString rangeOfString:@"swift"].location != NSNotFound) {
NSLog(@"Someone mentioned swift!");
}
复制代码

如果someStringnil,那rangeOfString: message方法会返回一个NSRange结构体的零值,也就是说.location为0,而NSNotFound被定义为NSIntegerMax。因此if语句也会执行。这显然不合理,因为nil字符串不包含"swift"。

Null引用带来了太多令人头疼的问题。Tony Hoare曾在1965年把之称为“带来几十亿美元损失的错误”。他批评道:

当时我为一个面向对象的语言(ALGOL W)设计第一个综合性的引用类型系统,我的本意是通过编译器的自动检查,确保所有的引用都是绝对安全的。但我没能抵挡住添加一个null引用的诱惑,仅仅是因为它非常容易实现。这导致了无数的错误和系统崩溃,在过去的四十年中可能导致了数十亿美元的损失。

#译者注

[1]:应该用parseInt()方法。getInteger()方法用于获取某个系统属性。具体解释参考:

Why does int num = Integer.getInteger(“123”) throw NullPointerException?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值