【问题标题】:Java - Using Generics for multiple TypesJava - 对多种类型使用泛型
【发布时间】:2014-08-15 13:13:22
【问题描述】:

我有以下 DataSet 类,我们可以在其中保存任何类型的对象。主要是将对象从服务器传输到客户端。 contents 变量可以保存任何可序列化的对象。它可以是自定义编写的类对象或 java 类,如 IntegerString 等。

我正在尝试将其转换为泛型。我们应该如何同时将其转换为泛型,我想确保我不必更改所有包含对象类的代码,因为我们甚至在内容。

我已经简化了这个类,只显示一些相关的字段和方法。

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.Serializable;
import java.util.Hashtable;
import java.util.Vector;

public class DataSet implements Serializable, PropertyChangeListener{

    Hashtable contents = new Hashtable();

    @Override
    public void propertyChange(PropertyChangeEvent arg0) {
        // TODO Auto-generated method stub

    }

    public void addObject(Object obj){
        Vector vector = (Vector)contents.get(obj.getClass());
        if (vector == null){
            vector = new Vector();
            vector.add(obj);
            contents.put(obj.getClass(), vector);
        }
    }

    public Vector getObjects(Class objType){
        if (contents.contains(objType)){
            return (Vector)contents.get(objType);
        }
        else{
            return new Vector();
        }
    }
}

感谢您的及时回复。但是,在查看了以下三个答案之后,我觉得我没有正确传达问题。

为了实现我想要的方式,如果我调用 dataset.getObjects(E 类型),我应该得到一个 E 类型的向量。我不应该投射到(矢量)中。因为我希望内容包含键值对的Class<E>Vector<E>。如果我们能做到,那我只能说这是safe

DataSet ds = new DataSet();
Vector<String> strings = ds.getObjects(String.class);
Vector<Integer> integer = ds.getObjects(Integer.class);
Vector<Employee> employees = ds.getObjects(Employee.class);

【问题讨论】:

    标签: java generics


    【解决方案1】:

    首先,远离类Vector,已经过时了;请改用ArrayList

    这是一种存储异构对象的简单方法,同时在检索对象时避免显式转换。我相信您可以将此模型应用到您的解决方案中。

    已更新,感谢 Paul Bellora 的建议。

    public class DataSet { 
    
        private Map<Class<?>, List<?>> contents;
    
        public DataSet(){
           contents = new HashMap<Class<?>, List<?>>();  
        } 
    
        @SuppressWarnings(value = { "unchecked" })
        public <T> void addObject(Class<T> klass, T object){
            List<T> objectsList = (List<T>)contents.get(klass);
            if (objectsList == null){
                objectsList = new ArrayList<T>();            
                contents.put(klass, objectsList);
            }
            objectsList.add(object);
        }
    
        @SuppressWarnings(value = { "unchecked" })
        public <T> List<T> getObjects(Class<T> klazz) {
            return contents.containsKey(klazz) ? 
                    contents.get(klazz) : Collections.EMPTY_LIST;
        }
    
        public static <S, T extends Iterable<S>> void print(T list){
            for (Object element : list){
                System.out.println(element);
            }
        }
    
        public static void main(String[] o){
           DataSet ds = new DataSet();
           ds.addObject(String.class, "save the earth!");
           ds.addObject(String.class, "firing now!");       
           ds.addObject(Integer.class, new Integer(100));
           ds.addObject(Boolean.class, new Boolean(true));
    
           print(ds.getObjects(String.class));
           print(ds.getObjects(Boolean.class));
           print(ds.getObjects(Integer.class));      
        }
     }
    

    希望对你有帮助。

    【讨论】:

    • 我更喜欢这个。我不知道您使用的语法“public T getObject(Class klass)”。唯一的事情是,我想持有一个 Class 作为该类型值的键和 Vector。不是对象。对象对我来说太通用了,被认为不安全。如果我们能做到这一点,我很好。这是我们在本地环境中拥有的一个巨大框架的一部分,要更改所有其他引用类并不容易。我同意我们可以拥有 ArrayList。转换成泛型对我来说没有什么不同。
    • 你是什么意思“持有一个类作为关键”?对象的类名,哈希码,什么?
    • Hashtable 将保存键值对,这样键将是某种类型,值将是该类型的向量。例如,Hashtable 将 1.java.lang.String 作为键,Vector 作为值。 2. custom.pkg.Employee 作为键,Vector 作为值。我希望你明白。正如您明确指出的那样,此 Hashtable 将具有不止一种类型的键值对。
    • 我只会使用Class&lt;?&gt; 作为键——这就是类型安全的异构容器模式通常的样子。无论如何,允许除了类名之外的其他字符串似乎有问题。
    【解决方案2】:

    如果我没记错的话,您的 Hashtable contents 变量将 Serializable 对象的类作为键,将可序列化的 Vector 列表作为值

       import java.beans.PropertyChangeEvent;
        import java.beans.PropertyChangeListener;
        import java.io.Serializable;
        import java.util.Hashtable;
        import java.util.Vector;
    
        public class DataSet implements Serializable, PropertyChangeListener{
    
            Hashtable<Class,Vector<Serializable> contents = new Hashtable<Class,Vector<Serializable>();
    
            @Override
            public void propertyChange(PropertyChangeEvent arg0) {
                // TODO Auto-generated method stub
    
            }
    
            public void addObject(Serializable obj){
    
                Vector<Serializable> vector = contents.get(obj.getClass());
                if (vector == null){
                    vector = new Vector<Serializable>();
                    vector.add(obj);
                    contents.put(obj.getClass(), vector);
                  }
                else{
                  vector.add(obj);
                   }
            }
    
            public Vector<Serializable> getObjects(Class objType){
                if (contents.contains(objType)){
                    return (Vector<Serializable>)contents.get(objType);
                }
                else{
                    return new Vector<Serializable>();
                }
            }
        }
    

    【讨论】:

    • 你的“Hashtable contents = new Hashtable();” @"Kumar Abhinav" 给我一个编译问题。
    • @user3656845 它是一个钻石运算符,仅在 Java 7 中新引入..阅读钻石运算符
    • @user3656845 两边都可以使用正常推理类型。
    • 我稍微修改了这个问题。当我调用 getObjects(Class) 时,我应该得到 Vector.
    • @user3656845 我认为我的程序非常适合上述情况
    【解决方案3】:

    或许这样就足够了:

    import java.beans.PropertyChangeEvent;
    import java.beans.PropertyChangeListener;
    import java.io.Serializable;
    import java.util.Map;
    import java.util.List;
    
    public class DataSet implements Serializable, PropertyChangeListener{
    
        Map<Class<?>, Vector<Serializable>> contents = new HashMap<Class<?>, Vector<Serializable>>();
    
        @Override
        public void propertyChange(PropertyChangeEvent arg0) {
            // TODO Auto-generated method stub
        }
    
        public void <T extends Serializable> addObject(T obj){
            Vector<T> vector = (Vector<T>)contents.get(obj.getClass());
            if (vector == null){
                vector = new Vector<T>();
                contents.put(obj.getClass(), vector);
            }
            vector.add(obj);
        }
    
        public <T extends Serializable> Vector<T> getObjects(Class<T> objType){
            if (contents.contains(objType)){
                return (Vector<T>)contents.get(objType);
            }
            else{
                return new Vector<T>();
            }
        }
    }
    

    我已经编辑了我的原始帖子。事实上,正如Duncan 在他的 cmets 中所指出的,DataSet 结构本身没有通用性的意义。尽管如此,一些泛型仍然可以添加一些语法糖来避免强制转换

    【讨论】:

    • 我不认为DataSet 应该是通用的,因为其目的是存储所有类型的对象。 FWIW,我不是你的反对者。
    • 如果Serializable对象,内容应该包含向量列表,通配符不能解决它。参见OP
    • @KumarAbhinav,你说得有道理,通配符在那里没有用。
    • 邓肯是对的。我不能使数据集通用。因为它可以容纳任何类型的物体。同样,我修改了问题。为了使这成为我想要的方式,如果我调用 dataset.getObjects(E 类型),我应该得到一个 E 类型的向量。我不应该投射到(矢量)中。因为我希望内容包含键值对的 Class 和 Vector。如果我们能做到这一点,那么只有我可以说这是安全的。我是期望太多还是试图做不可能的事??
    • @Duncan,确实你是对的。我已经理解了你的观点并删除了类级别的泛型。它似乎更适合方法级别,消费者将从避免的类型转换中受益
    猜你喜欢
    • 1970-01-01
    • 2012-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-02
    相关资源
    最近更新 更多