A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 张权 中级黑马   /  2013-4-28 10:08  /  1373 人查看  /  1 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 张权 于 2013-4-28 23:42 编辑

Enumeration<?> propertyNames( )为什么Properties类定义的这个方法, 这里要用通配符呢, 为什么不把类型变量改成String呢? 这是在JDK开发时的一个失误, 还是这里不能改成String? 如果不能改成String,那是为什么呢?
Set<String> stringPropertyNames()  这个方法泛型为什么又能指定类型变量为String呢?

评分

参与人数 1技术分 +1 收起 理由
黄玉昆 + 1

查看全部评分

1 个回复

倒序浏览
没人回答, 我自己把源码给改了下, 最终个人分析, 应该是源码的设计没有设计好, 下面是源码, 我把源码改成的类型变量的通配符改成了String, 结果运行不会出错.
public Enumeration<?> propertyNames() {
            Hashtable h = new Hashtable();
                 enumerate(h);
                 return h.keys();
    }
    private synchronized void enumerate(Hashtable h) {
            if (defaults != null) {
                    defaults.enumerate(h);
            }
           for (Enumeration e = keys(); e.hasMoreElements();) {
                    String key =  (String)e.nextElement();
                    h.put(key, get(key));
            }
    }

而且源码还有个很大的问题就是, 当我们用Properties对象添加属性时, 我们可以用它的父类Hashtable<Object, Object>的put<Key k, Value v>方法来添加, 如果用put添加, 那么就可以是添加任意类型的键值对了, 但是如果我们真的给键添加了其它类型(非String), 这样在使用propertyNames()方法来获取所有键的枚举时, 会发现有个ClassCastException,类型转换异常, 这个原因是因为当调用  propertryNames()方法时, 该方法内部会创建一个Hashtable对象, 该对象没有记录键值对, 而之前在使用put方法时, Hashtable中有键值对, 那么这时新的对象要拥有键值对,就要把之前put方法添加的键值对添加到新的Hashtable中, 但是在添加过程中, 源码做了一个强转动作   String key =  (String)e.nextElement(); 该强转动作要能正确执行, 那么也就是说put方法添加的那个老值就必须是String类型的才能强转. 所以我个人觉得这就是源码上的缺陷, 如果把源码中的 public Enumeration<?> propertyNames()方法中Enumeration的通配符改成String, 那么当用户使用时, 就不会去添加其它类型.  或者不改通配符, 那么就把强转给改以下, 改成步强转, 用Object来接受要传递给新Hashtable中的老值, 这样也不会出错, 下面是出错代码:
import java.util.Enumeration;
import java.util.Hashtable;


class Demo {
    public static void main(String[] ags) {
            Properties p = new Properties();
            p.put(1, "zhangsan");
            p.put(2, "zhangsan");
            p.put(3, "wangwu");
            Enumeration<String> keys = p.propertyNames();
            while (keys.hasMoreElements()) {
                    String key = (String) keys.nextElement();
                    System.out.println(key);
            }               
    }
}
  建议源码修改样式一:
class Properties extends Hashtable<Object, Object> {
    protected Properties defaults;

    public Enumeration<?> propertyNames() {
            Hashtable h = new Hashtable();
                 enumerate(h);
                 return h.keys();
    }

    private synchronized void enumerate(Hashtable h) {
            if (defaults != null) {
                    defaults.enumerate(h);
            }
            for (Enumeration e = keys(); e.hasMoreElements();) {
                       //这里不要强转, 用Object来接受, 那么使用Properties对象使用put方法添加元素
                    //不论添加什么元素, 使用枚举器遍历都不会报错.
                    Object key =  e.nextElement();
                    h.put(key, get(key));
            }
    }
}

源码修改样式二:
class Properties extends Hashtable<Object, Object> {
    protected Properties defaults;

    //这里把枚举的通配符改成String, 那么用户在使用Properties的put方法来添加键值对时,又想用propertyNames
    //方法来获取枚举器遍历,那么用户就不会添加其它类型的键, 只会添加String类型的
    //其实仅仅更改这里作用不大,
    public Enumeration<String> propertyNames() {
            Hashtable h = new Hashtable();
                 enumerate(h);
                 return h.keys();
    }

    private synchronized void enumerate(Hashtable h) {
            if (defaults != null) {
                    defaults.enumerate(h);
            }
            for (Enumeration e = keys(); e.hasMoreElements();) {
                     //这里依然用强转
                  String key =  (String)e.nextElement();
                   h.put(key, get(key));
            }
    }
}

个人认为, 应该是Sun已经放弃了修改这个问题, 因为在JDK1.2后,添加了专门供Properties类添加键值对的方法, 且方法做了限制, 参数类型只能是String类型, 所以, 我们使用时是根本就不用Hashtable中的方法来对Properties类进行各种操作了, 那么Sun公司也就懒得修改这个错误了, 这里也可以总结一下, 虽然要知道Properties是    Hashtable<Object, Object>的子类, 但是在使用它时, 我们应该把它当作一个独立的类来使用;

这个是我的个人总结, 其中有些是自己分析的, 有些问过别人, 总之就是我的个人理解及一些他人答案综合起来的, 如果有看到这个贴的, 我哪里理解错误的还望指出;
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马