最近在读阿里出品的 Java 开发手册,其中有一条规则是:

POJO 类中的任何布尔类型的变量,都不要加 is 前缀,否则部分框架解析会引起序列化错误。

这条规则让 iOS 开发出身的让感到好奇:为什么 Swift 推荐 Bool 用 is 前缀,而 Java 却要避免这种命名?

Java:JavaBeans 规范带来的解析差异

先从 Java 说起。JavaBeans 是一套基于命名约定的组件模型,核心是通过 getX()/isX()/setX() 的方法名推断 “属性名”,很多框架会借助 java.beans.IntrospectorPropertyDescriptor 进行反射解析并调用访问器。

因此 Java 生态广泛依赖 JavaBeans 规范,布尔字段如果直接叫 isReady,可能被推断为 readyisReady,在不同框架之间容易产生不一致。

private boolean isReady;

public boolean isReady() {
    return isReady;
}

public void setReady(boolean ready) {
    this.isReady = ready;
}

底层上,框架往往先解析出属性名,再按属性名绑定或序列化,这就是 is 前缀产生歧义的根因。

因此更稳妥的做法通常是:字段用 readyenabled 这类名,getter 用 isReady(),避免字段名本身带 is

Objective-C:getter 推荐 is,但属性名更像形容词

作为早期 iOS 开发的代表,Objective-C 的 @property 是编译期语法糖,编译器会生成 ivar 与 getter/setter,运行时通过 objc_msgSend 做消息分发,KVC 则按访问器和 ivar 规则查找属性。

Cocoa 命名规则建议 BOOL 的 getter 用 is / has / should,但属性名本身更倾向“形容词”。常见写法是用自定义 getter:

@property (nonatomic, getter=isHidden) BOOL hidden;

这样读取时是 isHidden,setter 仍然是 setHidden:,语义清晰,也避免属性名叫 isHidden 带来的 setter 命名怪异。

Swift:语义优先,不存在 JavaBeans 歧义

到了 Swift 的时代,属性访问是编译期语义。obj.property 对于存储属性会被编译为内存偏移的直接读写,计算属性则调用 getter/setter;类的动态行为主要由静态派发或虚表派发完成,只有标记 @objcdynamic 时才会走 Objective-C 运行时,因此不会依赖 JavaBeans 式的命名解析。

Swift 的 API 设计指南强调“读起来像断言”,所以 isEnabledisHidden 这类名字非常自然,也不会出现 is 前缀被剥离的歧义。

if view.isHidden {}

Python:属性访问直接对应名字

既然提起了这个问题,就连带着另外一个我经常使用的开发语言 Python 一起研究一下。

Python 的属性读取会进入 __getattribute__,按实例字典、类字典与描述符协议解析,@property 也是用描述符实现 getter/setter。由于查找基于字典与描述符,而不是命名约定,所以不会出现 JavaBeans 的歧义。

约定上常用 is_has_ 前缀表达布尔语义,但这只是命名习惯。

class User:
    def __init__(self):
        self.is_ready = False

小结

Java:字段避免 is 前缀,getter 用 isX()
Objective-C:属性名用形容词,getter 用 isX 更符合 Cocoa。
Swift:放心用 is/has/should,语义更强。
Python:用 is_ 只是风格选择,不涉及解析冲突。