【问题标题】:Putting objects into java.util.Properties将对象放入 java.util.Properties
【发布时间】:2015-05-21 18:25:49
【问题描述】:

我试图理解为什么 java.util.Properties 以这种方式实现。它有两个接口:getProperty/setProperty 只接受字符串,put/get 接受任何对象作为值。这两个接口看起来是重叠的,所以用 put() 添加的字符串可以使用 getProperty() 来检索。

这个奇怪的混合界面似乎存在一些问题。放置一个覆盖字符串属性的对象具有清除字符串值的副作用,产生 null 作为 getProperty 结果。添加一个整数或其他一些具有简单字符串转换的值,可能会被误解为一个真实的属性值(但作为一个属性,它始终为空)。

我的问题是:这是否有真正的实际原因?还是我怀疑它是一个半生不熟的实现?

【问题讨论】:

  • 我无法备份它,但我强烈怀疑问题是向后兼容性。 Properties 扩展了Hashtable,这是泛型之前的一个旧类;这意味着如果你有一个Properties,你可以调用properties.put(whatever, whateverElse)。当泛型出现时,Java 人员希望保持代码向后兼容,这意味着让它扩展 Hashtable<Object, Object>
  • PropertiesHashtable 的子类,没有覆盖get/put,因此有这种行为。 getProperty/setPropertyget/put 的类型化版本。这是历史问题,而不是隐藏 get/put。

标签: java


【解决方案1】:

Joshua BlochEffective Java 中明确提到了这一点

[来自第 4 章] 在 Properties 的情况下,设计者打算只允许字符串作为键和值,但直接访问底层 Hashtable 允许这个不变量是违反。一旦违反了这个不变量,就不能再使用 Properties API 的其他部分(loadstore)。等到发现这个问题时,再纠正它已经太晚了,因为客户端依赖于使用非字符串键和值。

该文本是在使用组合而不是继承的上下文中。他基本上以此为例说明何时应该使用组合而不是继承。如果Properties 包装了一个Map 而不是扩展一个,它可能会强制使用String 作为键和值。

所以答案是:这是一个疏忽。

【讨论】:

  • 选择这个答案是因为它解释了为什么以这种方式实现,而不仅仅是“不要使用其他方法”。附带说明一下,java 邮件 API 设计使用它来配置自定义 SSL 套接字工厂。哦,java,我们之间的爱恨交织。
【解决方案2】:

访问putgetPropertiesHashtable 的扩展的结果,这两种方法不应使用(但由于它们在超类中的公共访问权限,因此不能从实现中隐藏)。

Javadocs 很好地说明了为什么您不应该使用这些方法,而应该只使用字符串:

因为Properties 继承自Hashtable,所以putputAll 方法可以应用于Properties 对象。强烈建议不要使用它们,因为它们允许调用者插入键或值不是Strings 的条目。应该改用setProperty 方法。如果在包含非String 键或值的“受损”Properties 对象上调用storesave 方法,则调用将失败。类似地,如果在包含非String 键的“受损”Properties 对象上调用propertyNameslist 方法,则会失败。

正如@yshavit 所说,Properties 扩展 Hashtable<String, String> 比扩展两个对象的哈希表更有意义,但这可能是为了保持向后兼容性而做出的决定,就像任何使用 get/ 的程序一样put 与任何非字符串对象都会被这样的更改破坏。

【讨论】:

  • 但这并没有解决问题的关键部分,这就是为什么 get/put 方法适用于 Object 而不是 String。也就是说,为什么不扩展Hashtable<Object, Object>? (根据我上面的评论,我强烈怀疑这是为了向后兼容——但我没有具体的证据。)
  • @yshavit 好记;我同意这几乎肯定是为了向后兼容,我在答案中添加了这样的注释。
【解决方案3】:

官方文档说

因为 Properties 继承自 Hashtable,所以 put 和 putAll 方法可以应用于 Properties 对象。强烈建议不要使用它们,因为它们允许调用者插入键或值不是字符串的条目。应该改用 setProperty 方法。如果在包含非字符串键或值的“受损”属性对象上调用存储或保存方法,则调用将失败。类似地,如果在包含非字符串键的“受损”属性对象上调用 propertyNames 或 list 方法,则调用将失败。

http://docs.oracle.com/javase/8/docs/api/java/util/Properties.html

【讨论】:

    【解决方案4】:

    来自Java Docs

    因为 Properties 继承自 Hashtable,所以 put 和 putAll 方法 可以应用于 Properties 对象。他们的使用非常强烈 不鼓励,因为它们允许调用者插入其键或 值不是字符串。应该改用 setProperty 方法。 如果在“受损”属性上调用 store 或 save 方法 包含非字符串键或值的对象,调用将失败。 同样,对 propertyNames 或 list 方法的调用将失败,如果 它在包含一个“妥协”的 Properties 对象上调用 非字符串键。

    【讨论】:

      【解决方案5】:

      Properties 扩展了Hashtable,因此您可以在任何可以使用Hashtable 的地方使用Properties

      Hashtable 类是 get()put() 函数的来源。

      【讨论】:

        【解决方案6】:

        put(key, value)get(key) 是让Properties 扩展Hashtable 回到过去的可疑决定的剩余部分。这种行为不能改变,因为它会破坏向后兼容性,但任何半体面的风格指南,包括 API 文档本身,都建议不要使用这些方法。

        【讨论】:

          【解决方案7】:

          正如其他人所说,您只能将它用于字符串。有很多方法可以将对象序列化为字符串并检索它,但显然它不是为此而设计的。我知道这真的很烦人,因为它是最接近跨平台保存和检索应用程序数据的方式。据我所知,尽管大多数操作系统都有 appdata 目录,但没有官方方法可以将字符串以外的内容保存在对用户隐藏的文件夹中。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-01-01
            • 2015-08-30
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多