第六十二条:如果其他类型更适合,则尽量避免使用字符串

字符串被用来表示文本,它在这方面也确实做得很好。因为字符串很通用,并且Java语言也支持得很好,所以自然就会有这样一种倾向:即便在不适合使用字符串的场合,人们往往也会使用字符串。本条目就是讨论一些不应该使用字符串的情形。

字符串不适合代替其他的值类型。当一段数据从文件、网络,或者键盘设备,进入程序之后,它通常以字符串的形式存在。有一种自然的倾向是让它继续保留这种情形,但是,只有当这段数据本质上确实是文本信息时,这种想法才是合理的。如果它是数值,就应该被转换为适当的数值类型,比如int、float或者BigInteger类型。如果它是一个“是 - 或 - 否”这种问题的答案,就应该被转换为boolean类型。如果存在适当的值类型,不管是基本类型,还是对象引用,大多应该使用这种类型;如果不存在这样的类型,就应该编写一个类型。虽然这条建议是显而易见的,但通常未能得到遵守。

字符串不适合代替枚举类型。正如第34条中所讨论的,枚举类型比字符串更加适合用来表示枚举类型的常量。

字符串不适合代替聚合类型。如果一个实体有多个组件,用一个字符串表示这个实体通常是很不恰当的。例如,下面这行代码来自于真实的系统,标识符的名称已经被修改了,以免发生纠纷:

// Inappropriate use of string as aggregate type
String compoundKey = className + "#" + i.next();

这种方法有许多缺点。如果用来分隔的字符也出现在某个域中。结果就会出现混乱。为了访问单独的域,必须解析该字符串,这个过程非常慢,也很烦琐,还容易出错。你无法提供equals、toString或者compareTo方法,只好被迫接受String提供的行为。更好的做法是,简单的编写一个类来描述这个数据集,通过是一个私有的静态成员类(详见第24条)。

字符串也不适合代替能力表。有时候,字符串被用于对某种功能进行授权访问。例如,考虑设计一个提供线程局部变量的机制。这个机制提供的变量在每个线程中都有自己的值。自Java1.2发行版本以来,Java类库就有提供线程局部变量的机制,但在那之前,程序员必须自己完成。几年前,面对这样的设计任务时,有些人提出了同样的设计方案:利用客户提供的字符串键对每个线程局部变量的内容进行访问授权:

// Broken - inappropriate use of string as capability!
public class ThreadLocal {
  private ThreadLocal() { } // Noninstantiable
  // Sets the current thread's value for the named variable. 
  public static void set(String key, Object value);
  // Returns the current thread's value for the named variable.
  public static Object get(String key); 
}

这种方法的问题在于,这些字符串键代表了一个共享的全局命名空间。要使这种方法可行,客户端提供的字符串键必须是唯一的:如果两个客户端各自决定为它们的线程局部变量使用同样的名称,它们实际上就无意中共享了这个变量,这样往往会导致两个客户端都失败,而且安全性也很差。恶意的客户端可能有意的使用与另一个客户端相同的键,以便非法的访问其他客户端的数据。

要修正这个API并不难,只要用一个不可伪造的键(有时被称为能力)来代替字符串即可:

public class ThreadLocal {
  private ThreadLocal() { } // Noninstantiable
  public static class Key { // (Capability) 
     Key() { }
  }
  // Generates a unique, unforgeable key 
  public static Key getKey() { return new Key(); }
  public static void set(Key key, Object value);
  public static Object get(Key key); 
}

这样虽然解决了基于字符串的API的两个问题,但是你还可以做得更好。你实际上不再需要静态方法,它们可以变成键上的实例方法,这样这个键就不再是键,而是线程局部变量了,此时,这个不可被实例化的顶层类也不再做任何实质性的工作,因此可以删除这个顶层类,并将嵌套类命为ThreadLocal:

public final class ThreadLocal { 
  public ThreadLocal();
  public void set(Object value); 
  public Object get();
}

这个API不是类型安全的,因为当你从线程局部变量得到它时,必须将值从Object转换成它实际的值。不可能使原始的基于String的API为类型安全的,要使基于Key的API为类型安全的也很困难,但是通过将ThreadLocal类泛化(详见第29条),使这个API变成类型安全的就是很简单的事情了:

public final class ThreadLocal<T> { 
  public ThreadLocal();
  public void set(T value);
  public T get();
}

粗略的讲,这正是java.lang.ThreadLocal提供的API。除了解决了基于字符串的API的问题之外,与前面的两个基于键的API相比,它还更快速、更美观。

总而言之,如果可以使用更加适合的数据类型,或者可以编写更加适当的数据类型,就应该避免用字符串来表示对象。若使用不当,字符串会比其他的类型更加笨拙、更加不灵活、速度更慢,也更容易出错。经常被错误的用字符串来代替的类型包括基本类型、枚举类型和聚合类型。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容