概述

ClassToInstanceMap提供了一种是用Class作为Key, 对应实例作为Value的途径.他定义了T getInstance(Class<T>)和T putInstance(Class<T> T)两个方法, 这两个方法消除了元素类型转换的过程并保证了元素在Map中是类型安全的.

ClassToInstanceMap有一个独立的类型参数, 一般命名为B. 它对应着Map的元素的类型的最大上界.例如

ClassToInstanceMap<Number> numberDefaults = MutableClassToInstanceMap.create();
numberDefaults.putInstance(Integer.class, Integer.valueOf(0));

实现上, ClassToInstanceMap<B>实现了Map<Class<? extends B>, B> -- 换句话说, 他是一个由B的子类和B的实例构成的Map.这让泛型在ClassToInstanceMap里有点混乱,但是只需要记住B是所有map中的类型的上界就够了 -- 通常, B就是Object

Guava提供了很有用的ClassToInstanceMap的实现 MutableClassToInstanceMap 和 ImmutableClassToInstanceMap

重点: 就像其他 Map<Class, Object>, ClassToInstanceMap可能会包含原生类型的元素, 原生类型和它的包装类在map中可能会映射到不同的值上.但是在getInstance取值的时候会将所有原生类型都转成它的包装类.

MutableClassToInstanceMap

构造方法

    /**
* 返回一个使用new HashMap<Class<? extends B>, B>()作为代理的MutableClassToInstanceMap
* 内部调用的是MutableClassToInstanceMap(Map<Class<? extends B>, B> delegate)这个私有构造方法
*/
public static <B> MutableClassToInstanceMap<B> create() {
return new MutableClassToInstanceMap<B>(
new HashMap<Class<? extends B>, B>());
} /**
* 通过已存在的new HashMap<Class<? extends B>, B>()作为代理构造MutableClassToInstanceMap
* 内部调用的是MutableClassToInstanceMap(Map<Class<? extends B>, B> delegate)这个私有构造方法
*/
public static <B> MutableClassToInstanceMap<B> create(
Map<Class<? extends B>, B> backingMap) {
return new MutableClassToInstanceMap<B>(backingMap);
} /**
* 私有构造方法, 通过delegate和MapConstraint<Class<?>, Object>来构造ConstrainedMap并返回
* @param delegate
*/
private MutableClassToInstanceMap(Map<Class<? extends B>, B> delegate) {
super(delegate, VALUE_CAN_BE_CAST_TO_KEY);
} /**
* 用来保证当你没有指定MutableClassToInstanceMap<B>的B类型时
* 在V put(K key, V value)的时候V的Class是K的子类
*/
private static final MapConstraint<Class<?>, Object> VALUE_CAN_BE_CAST_TO_KEY
= new MapConstraint<Class<?>, Object>() {
@Override
public void checkKeyValue(Class<?> key, Object value) {
cast(key, value);
}
}; /**
* cast()方法实际上做的事情是对原生类型的Class做一次包装
* 并且调用Class.cast()方法,这样如果type和value对不上,则会抛出ClassCastException
*/
private static <B, T extends B> T cast(Class<T> type, B value) {
return Primitives.wrap(type).cast(value);
}

从这几个构造方法可以看出MutableClassToInstanceMap是使用代理实现的Map, 他使用了一个MapConstraint来限制当一个MCTIMap没有指定Class上界的时候put进去的Value的Class与Key的继承合法性,cast()方法会对value做一次type的cast,这样如果put进去的Value的Class不是Key的子类就会抛出异常,这也是fast fail的一种形式

get与put

MutableClassToInstanceMap自己实现的getInstance与putInstance

    @Override
public <T extends B> T putInstance(Class<T> type, T value) {
return cast(type, put(type, value));
} @Override
public <T extends B> T getInstance(Class<T> type) {
return cast(type, get(type));
}

这两个方法都调用了cast()方法做了一次原生类型包装与类型转换(检查),下面在看看 get() 与 put() 的实现

其中,get()使用的是ForwardingMap的get()方法

  @Override
public V get(Object key) {
return delegate().get(key);
}

这个方法很简单,也就是直接调用了delegate的get()方法,实际上就是HashMap的get()

put()方法使用的是ConstrainedMap的put()方法

    @Override public V put(K key, V value) {
constraint.checkKeyValue(key, value);
return delegate.put(key, value);
}

这个方法调用了MapConstraint的checkKeyValue,保证了put进去的Value的类型的正确性

不过一般来说,使用ClassToInstanceMap应该调用getInstance和putInstance, 而不是get()和put(), 下面是一个使用代码示例

        MutableClassToInstanceMap<Number> map = MutableClassToInstanceMap.create();
map.putInstance(Integer.class, 100);
map.putInstance(Float.class, 10.01f);
System.out.println(map.getInstance(Integer.class));
System.out.println(map.getInstance(Float.class));

ImmutableClassToInstanceMap

ImmutableClassToInstanceMap顾名思义就是不可变更的ClassToInstanceMap, 我们在对这个Map构造完成后边不可再变更

它的使用和MutableClassToInstanceMap大同小异,只不过在构造完成后在调用put()或者putInstance()会抛出UnsupportedOperationException

使用示例

        ImmutableClassToInstanceMap<Number> map =
new ImmutableClassToInstanceMap.Builder<Number>()
.put(Integer.class, 100)
.put(Float.class, 10.01f)
.build();
ImmutableClassToInstanceMap<Number> map2 = ImmutableClassToInstanceMap.copyOf(map);
// throws UnsupportedOperationException
// map.putInstance(Integer.class, 1000);
// map.put(Integer.class, 1000);
System.out.println(map.getInstance(Integer.class));
System.out.println(map2.getInstance(Float.class));

之所以使用Builder来创建ImmutableClassToInstanceMap,是因为在创建的时候是可以put()的,而创建完以后返回的是另外一个类型的Map,他的put()方法被重写为直接抛出UnsupportOperationException

总结

我们之所以使用ClassToInstanceMap而不是使用Map<Class, Object>,就是因为ClassToInstanceMap使用了MapConstraint, 他保证了我们放入的Class和Object的类型是对应的, 而不会出现 put(Integer.class, "string")这样的情况.

Guava ClassToInstanceMap的更多相关文章

  1. 强大的Guava中的新集合类型: Multiset, Multimap, BiMap, Table, ClassToInstanceMap, RangeSet, RangeMap等

    一 Multiset /** * 新类型集合: Multiset: Multiset就是可以保存多个相同的对象,并且无序 * 占据了List和Set之间的一个灰色地带 * 其他实现: TreeMult ...

  2. Guava学习笔记:Guava新集合-Table等

    Table 当我们需要多个索引的数据结构的时候,通常情况下,我们只能用这种丑陋的Map<FirstName, Map<LastName, Person>>来实现.为此Guava ...

  3. Guava学习笔记:Guava新增集合类型-Multiset

    Guava引进了JDK里没有的,但是非常有用的一些新的集合类型.所有这些新集合类型都能和JDK里的集合平滑集成.Guava集合非常精准地实现了JDK定义的接口.Guava中定义的新集合有: Multi ...

  4. Guava学习笔记:Immutable(不可变)集合

    不可变集合,顾名思义就是说集合是不可被修改的.集合的数据项是在创建的时候提供,并且在整个生命周期中都不可改变. 为什么要用immutable对象?immutable对象有以下的优点: 1.对不可靠的客 ...

  5. guava 学习笔记(二) 瓜娃(guava)的API快速熟悉使用

    guava 学习笔记(二) 瓜娃(guava)的API快速熟悉使用 1,大纲 让我们来熟悉瓜娃,并体验下它的一些API,分成如下几个部分: Introduction Guava Collection ...

  6. Guava 集合框架

    在本系列中我们首先来学习一些Guava的集合框架,也就是这个package:com.google.common.collect 在这个包下面有一些通用的集合接口和一些相关的类.   集合类型: BiM ...

  7. guava学习--集合2&Range

    转载:http://www.cnblogs.com/peida/p/Guava_ImmutableCollections.html Table: 当我们需要多个索引的数据结构的时候,通常情况下,我们只 ...

  8. 瓜娃《guava》api快速入门

    1,大纲 让我们来熟悉瓜娃,并体验下它的一些API,分成如下几个部分: Introduction Guava Collection API Guava Basic Utilities IO API C ...

  9. [Guava官方文档翻译] 7. Guava的Immutable Collection(不可变集合)工具 (Immutable Collections Explained)

    我的技术博客经常被流氓网站恶意爬取转载.请移步原文:http://www.cnblogs.com/hamhog/p/3538666.html ,享受整齐的排版.有效的链接.正确的代码缩进.更好的阅读体 ...

随机推荐

  1. InnoDB的锁机制浅析(三)—幻读

    文章总共分为五个部分: InnoDB的锁机制浅析(一)-基本概念/兼容矩阵 InnoDB的锁机制浅析(二)-探索InnoDB中的锁(Record锁/Gap锁/Next-key锁/插入意向锁) Inno ...

  2. 一个页面从输入URL 到页面加载显示完成,这个过程中都发生了什么?

    1.当发送一个URL请求时,浏览器会开启一个线程来处理这个请求,同时在远程DNS服务器上启动一个DNS查询,解析获取网址的IP地址:2.浏览器与远程Web服务器通过TCP三次握手协商来建立一个TCP/ ...

  3. 2977 二叉堆练习1 codevs

    题目描述 Description 已知一个二叉树,判断它是否为二叉堆(小根堆) 输入描述 Input Description 二叉树的节点数N和N个节点(按层输入) 输出描述 Output Descr ...

  4. zoj 3659 第37届ACM/ICPC 长春赛区现场赛E题 (并查集)

    题意:给出一棵树,找出一个点,求出所有点到这个点的权值和最大,权值为路径上所有边权的最小值. 用神奇的并查集,把路按照权值从大到小排序,然后用类似Kruskal的方法不断的加入边. 对于要加入的一条路 ...

  5. 工具使用-----Jmeter的基础用法

    //摘抄至http://www.cnblogs.com/TankXiao/p/4045439.html 以下是我自己录制的关于这篇文章的视频,有兴趣的可以下载哦 https://yunpan.cn/c ...

  6. Codeforces Round #515 (Div. 3)

    Codeforces Round #515 (Div. 3) #include<bits/stdc++.h> #include<iostream> #include<cs ...

  7. Android学习笔记PreferenceFragment的使用

    相信大家对Perference都比较熟悉了,也就是我们常说的偏好设置,首选项设置,可以保存一些数据,例如我们在上一次使用的时候的一些内容,希望在下一次启动后依然生效,而不需要再进行配置那么麻烦.一般这 ...

  8. POJ 3076 SUKODU [Dangcing Links DLX精准覆盖]

    和3074仅仅有数目的不同,3074是9×9.本来想直接用3074的.然后MLE,,,就差那么20M的空间,,. 从这里学习到了解法: http://www.cnblogs.com/ylfdrib/a ...

  9. 得到Revit子窗体

    start /// <summary> /// 得到主窗体句柄 /// </summary> /// <returns></returns> publi ...

  10. Why I Left the .NET Framework

    The .NET Framework was good. Really good. Until it wasn't. Why did I leave .NET? In short, it constr ...