集合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>,在上述的实现中。
 

Item 29 优先考虑类型安全的异构容器的更多相关文章

  1. Effective Java 第三版——33. 优先考虑类型安全的异构容器

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  2. EffectiveJava(29)优先考虑类型安全的异构容器

    当你的泛型集合需要更多的灵活性,你可以将键进行参数化而不是将容器进行参数化.然后将参数化的键提交给容器,来插入或者获取值.用泛型系统来确保值得类型与它的键相符. 我们创建一个Favorite类来模拟这 ...

  3. 【Effective Java】8、优先考虑类型安全的异构容器

    有的时候我们一个容器只有一个类型或几个类型并不能满足我们的要求,比如set中存放的元素类型都是同一种,map也就指定的两种 这里我们可以将键进行参数化,而不是将容器参数化,也就是我们可以给容器传一个键 ...

  4. JSON 序列化与反序列化(二)使用TypeReference 构建类型安全的异构容器

    1. 泛型通常用于集合,如Set和Map等.这样的用法也就限制了每个容器只能有固定数目的类型参数,一般来说,这也确实是我们想要的. 然而有的时候我们需要更多的灵活性,如数据库可以用任意多的Column ...

  5. Effective Java 第三版——29. 优先考虑泛型

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  6. 读书笔记 effctive c++ Item 20 优先使用按const-引用传递(by-reference-to-const)而不是按值传递(by value)

    1. 按值传递参数会有效率问题 默认情况下,C++向函数传入或者从函数传出对象都是按值传递(pass by value)(从C继承过来的典型特性).除非你指定其他方式,函数参数会用实际参数值的拷贝进行 ...

  7. 读书笔记 effective c++ Item 29 为异常安全的代码而努力

    异常安全在某种意义上来说就像怀孕...但是稍微想一想.在没有求婚之前我们不能真正的讨论生殖问题. 假设我们有一个表示GUI菜单的类,这个GUI菜单有背景图片.这个类将被使用在多线程环境中,所以需要mu ...

  8. Effective JavaScript Item 46 优先使用数组而不是Object类型来表示有顺序的集合

    本系列作为Effective JavaScript的读书笔记. ECMAScript标准并没有规定对JavaScript的Object类型中的属性的存储顺序. 可是在使用for..in循环对Objec ...

  9. Effective JavaScript Item 31 优先使用Object.getPrototypeOf,而不是__proto__

    本系列作为Effective JavaScript的读书笔记. 在ES5中引入了Object.getPrototypeOf作为获取对象原型对象的标准API.可是在非常多运行环境中.也提供了一个特殊的_ ...

随机推荐

  1. spring学习(一)——控制反转简单例子

    spring框架是一个开源的轻量级的基于IOC与AOP核心技术的容器框架,主要是解决企业的复杂操作实现. 那IOC与AOP,到底如何解释呢,在看spring视频中,两个专业术语一定必须要懂得. IOC ...

  2. UCP协议

    UDP只在ip数据报的服务上增加了一点功能,就是复用和分用还有差错检验的功能 (1)UDP是面向无连接:发送之前不需要建立连接,减少了时间延续 (2)UDP只是尽最大努力交付,不能保证无措 (3)UD ...

  3. 《C陷阱与缺陷》之1词法"陷阱"

    编译器中负责将程序分解为一个一个符号的部分,一般称为"词法分析器".在C语言中,符号之间的空白(包括空格符.制表符或换行符)将被忽略. 1.=不同于== C语言使用符号" ...

  4. week1 技术随笔

    类别c 内容c 开始时间s 结束时间e 被打断时间I 总计(min) 9.5 随笔 构建之法福后感 22:00 24:00 7 113 9.6 分析 需求分析 9:00 9:30 2 28 编码 词频 ...

  5. animate.css与wow.js制作网站动效

    animate.css 官网:https://daneden.github.io/animate.css/ 包括:attention seekers:关注者 bouncing entrances:跳跃 ...

  6. 火狐浏览器(FireFox)安装Flash插件失败处理方法

    最近不知道怎么了,总是嫌弃IE,可能是被网络流量监测的网址给搞得了,弄了火狐浏览器,也安装了猎豹,这里不对浏览器做评价 好多朋友安装好火狐(FireFox)的时候发现之前不是有装IE的Flash播放插 ...

  7. QT分析之网络编程

    原文地址:http://blog.163.com/net_worm/blog/static/127702419201002842553382/ 首先对Windows下的网络编程总结一下: 如果是服务器 ...

  8. 简述在akka中发送消息的过程

    在flink的数据传输过程中,有两类数据,一类数据是控制流数据,比如提交作业,比如连接jm,另一类数据是业务数据.flink对此采用了不同的传输机制,控制流数据的传输采用akka进行,业务类数据传输在 ...

  9. 关联容器 // append方法

    关联容器和顺序容器的差别在于:关联容器通过键(key)存储和读取元素,而顺序容器则通过元素在容器中的位置顺序存储和访问元素. 1.关联容器支持通过键来高效地查找和读取元素.两个基本的关联容器类型是ma ...

  10. BZOJ 1816 扑克牌(二分)

    由于答案具有单调性,考虑二分答案并验证. 如果能凑齐x堆,因为每个joke在一个牌堆里最多只能用一次,则至多只能用min(x,m)个joke. 对于每个牌,如果这个牌的总数小于x,用joke补齐剩下的 ...