• Item 29 优先考虑类型安全的异构容器


    集合API展示了泛型的一般用法。但是它们(Set,HashMap,Map)限制了每个容器只能有固定数目的类型参数。

     
     
    比如Set集合,HashMap集合:
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.Map;
    import java.util.Set;
     
    public class NormalUse {
     
     
      public static void main(String[] args) {
       
        Set<String> oneSet = new HashSet<String>();
        oneSet.add("One");
        oneSet.add("Two");
       
       
        Map<String, Integer> oneMap = new HashMap<String, Integer>();
        oneMap.put("One", 1);
        oneMap.put("Two", 2);
        //Set集合,只有一个类型参数;HashMap集合有两个类型参数
      
      }
     
     
    }
     
     
    异构容器:
     
    如果一个容器里面它存放的类型参数数目是不固定的,那么它就是一个异构的容器。这是有使用场景的,比如,使用一个容器来存放数据库中有任意列的一个行。因为一行,列的数目是不固定的,每个列的类型也是不确定的,那么就可以使用一个异构的容器来表示这个行。
     
    一个列元素,用一个键值对来表示,并且键和值之间有有类型关系,键的类型跟值的类型是相同的。因为,有不同类型的列。在实现这个目标时,让键参数化(也就是键可以表示不同的类型,而不是容器参数化,Set<T>是容器参数化)。
     
     
    下述代码的实现分析:
     
    Map<Column<?>,Object> row = new HashMap<>();
    这个是实际存放不同的键值对。
    不同的键对应不同的值。
    因为只是对键进行类型参数化,不是对值进行类型参数。但是值要保证可以表示所有类型,那么值就采用所有对象的根类(Object)。
     
    Map包含了某个行的所有列。
     
     
     public <T> void putColumn(Column<T> type, T instance) {
        row.put(type, instance);
      }
    这个泛型方法,表示了键和值的对应关系。键的类型跟值的类型是相同的,都是T。
     
     
      public <T> T getColumn(Column<T> type) {
        return type.cast( row.get(type));
      }
    这个方法,是为了得到键对应的值类型,存放的值类型转换为添加时的类型。转换失败会抛出ClassCastException异常。
     
    如此,就可以使用一个容器来存放不同的元素类型,在这里表示为数据库中的某个行。而Set,HashMap,这样的容器只能存放一种元素类型。
     
     
    public class Column<T> {
     
      private final T valClass ;
     
      @SuppressWarnings( "unchecked" )
      public Column(Class<T> valClass) {
        this. valClass = (T) valClass;
      }
     
      @SuppressWarnings( "unchecked" )
      public T cast(Object obj) {
        return obj == null ? null : ((Class<T>) valClass).cast(obj);
      }
    }
     
    public class DatabaseRow {
     
      private Map<Column<?>, Object> row = new HashMap<>();
     
      public <T> void putColumn(Column<T> type, T instance) {
        if (type == null )
          throw new NullPointerException("Type is null" );
        row.put(type, instance);
      }
     
      public <T> T getColumn(Column<T> type) {
        return type.cast( row.get(type));
      }
     
      public static void main(String[] args) {
        DatabaseRow db = new DatabaseRow();
     
        Column<Integer> colInt = new Column<Integer>(Integer. class);
        Column<Double> colDouble = new Column<Double>(Double. class);
        Column<Float> colFloat = new Column<Float>(Float. class);
     
        db.putColumn(colInt, 1);
        db.putColumn(colDouble, 10.0);
        db.putColumn(colFloat, 12.3f);
     
        System. out.println(colInt.getClass() + " " + colDouble.getClass());
        System. out.println(db.getColumn(colInt) + " " + db.getColumn(colDouble));
      }
     
    }
     
    --------------------------------------------------------------------------------------------------------
     
    另外一个例子:
     
    实现一个容器,它存放的是任意类的实例。
    public class Favorites {
      // Typesafe heterogeneous container pattern - implementation
      private Map<Class<?>, Object> favorites = new HashMap<Class<?>, Object>();
     
      public <T> void putFavorite(Class<T> type, T instance) {
        if (type == null )
          throw new NullPointerException("Type is null" );
        favorites.put(type, instance);
      }
     
      public <T> T getFavorite(Class<T> type) {
        return type.cast( favorites .get(type));
      }
     
      // Typesafe heterogeneous container pattern - client
      public static void main(String[] args) {
        Favorites f = new Favorites();
        f.putFavorite(String. class , "Java" );
        f.putFavorite(Integer. class , 0xcafebabe);
        f.putFavorite(Class. class , Favorites. class);
     
        String favoriteString = f.getFavorite(String. class );
        int favoriteInteger = f.getFavorite(Integer. class );
        Class<?> favoriteClass = f.getFavorite(Class. class );
        System. out.printf( "%s %x %s%n" , favoriteString, favoriteInteger, favoriteClass.getName());
      }
    }
     
    ----------------------------------------------------------------------------------------------
     
    总结:
    要实现一个异构的容器,实现方式通常是使用一个键值对,这是因为底层的实现采用Map,然后,对键进行类型参数化,并且指定键和值之间的类型关系。这样就可以实现一个异构的容器,一个容器里面有不同的元素类型,而不是只有一个唯一的类型。另外,还可以对键进行"? extends T"的类型限制。
    通过让对键进行类型参数化,这样,新形成的容器,在外部看来就是可以存放不同的类型。虽然,在实现方面,它使用的Map,依然是秉持只能存放一种类型,这种类型就是<Class<?>,Object>,在上述的实现中。
     
  • 相关阅读:
    google code jam exercise——Your Rank Is Pure
    C/C++中的文件操作(2)
    google code jam exercise——File Fix It
    google code jam exercise——All Your Base
    C/C++中的文件操作(1)
    google code jam exercise——Center of Mass
    C/C++字符串操作split
    JavascriptDOM
    Javascript特权方法
    Report子报表
  • 原文地址:https://www.cnblogs.com/ttylinux/p/4440196.html
Copyright © 2020-2023  润新知