译者:万天慧(武祖)

由于类型擦除,你不能够在运行时传递泛型类对象——你可能想强制转换它们,并假装这些对象是有泛型的,但实际上它们没有。

举个例子:

ArrayList<String> stringList = Lists.newArrayList();
ArrayList<Integer> intList = Lists.newArrayList();
System.out.println(stringList.getClass().isAssignableFrom(intList.getClass()));
returns true, even though ArrayList<String> is not assignable from ArrayList<Integer>

Guava提供了TypeToken, 它使用了基于反射的技巧甚至让你在运行时都能够巧妙的操作和查询泛型类型。想象一下TypeToken是创建,操作,查询泛型类型(以及,隐含的类)对象的方法。

Guice用户特别注意:TypeToken与类GuiceTypeLiteral很相似,但是有一个点特别不同:它能够支持非具体化的类型,例如T,List<T>,甚至是List<? extends Number>;TypeLiteral则不能支持。TypeToken也能支持序列化并且提供了很多额外的工具方法。

背景:类型擦除与反射

Java不能在运行时保留对象的泛型类型信息。如果你在运行时有一个ArrayList<String>对象,你不能够判定这个对象是有泛型类型ArrayList<String>的 —— 并且通过不安全的原始类型,你可以将这个对象强制转换成ArrayList<Object>。

但是,反射允许你去检测方法和类的泛型类型。如果你实现了一个返回List的方法,并且你用反射获得了这个方法的返回类型,你会获得代表List<String>的ParameterizedType

TypeToken类使用这种变通的方法以最小的语法开销去支持泛型类型的操作。

介绍

获取一个基本的、原始类的TypeToken非常简单:

TypeToken<String> stringTok = TypeToken.of(String.class);
TypeToken<Integer> intTok = TypeToken.of(Integer.class);

为获得一个含有泛型的类型的TypeToken —— 当你知道在编译时的泛型参数类型 —— 你使用一个空的匿名内部类:

TypeToken<List<String>> stringListTok = new TypeToken<List<String>>() {};

或者你想故意指向一个通配符类型:

TypeToken<Map<?, ?>> wildMapTok = new TypeToken<Map<?, ?>>() {};

TypeToken提供了一种方法来动态的解决泛型类型参数,如下所示:

static <K, V> TypeToken<Map<K, V>> mapToken(TypeToken<K> keyToken, TypeToken<V> valueToken) {
return new TypeToken<Map<K, V>>() {}
.where(new TypeParameter<K>() {}, keyToken)
.where(new TypeParameter<V>() {}, valueToken);
}
...
TypeToken<Map<String, BigInteger>> mapToken = mapToken(
TypeToken.of(String.class),
TypeToken.of(BigInteger.class)
);
TypeToken<Map<Integer, Queue<String>>> complexToken = mapToken(
TypeToken.of(Integer.class),
new TypeToken<Queue<String>>() {}
);

注意如果mapToken只是返回了new TypeToken>(),它实际上不能把具体化的类型分配到K和V上面,举个例子

class Util {
static <K, V> TypeToken<Map<K, V>> incorrectMapToken() {
return new TypeToken<Map<K, V>>() {};
}
}
System.out.println(Util.<String, BigInteger>incorrectMapToken());
// just prints out "java.util.Map<K, V>"

或者,你可以通过一个子类(通常是匿名)来捕获一个泛型类型并且这个子类也可以用来替换知道参数类型的上下文类。

abstract class IKnowMyType<T> {
TypeToken<T> type = new TypeToken<T>(getClass()) {};
}
...
new IKnowMyType<String>() {}.type; // returns a correct TypeToken<String>

使用这种技术,你可以,例如,获得知道他们的元素类型的类。

查询

TypeToken支持很多种类能支持的查询,但是也会把通用的查询约束考虑在内。

支持的查询操作包括:

方法 描述
getType() 获得包装的java.lang.reflect.Type.
getRawType() 返回大家熟知的运行时类
getSubtype(Class<?>) 返回那些有特定原始类的子类型。举个例子,如果这有一个Iterable并且参数是List.class,那么返回将是List。
getSupertype(Class<?>) 产生这个类型的超类,这个超类是指定的原始类型。举个例子,如果这是一个Set并且参数是Iterable.class,结果将会是Iterable。
isAssignableFrom(type) 如果这个类型是 assignable from 指定的类型,并且考虑泛型参数,返回true。List<? extends Number>是assignable from List,但List没有.
getTypes() 返回一个Set,包含了这个所有接口,子类和类是这个类型的类。返回的Set同样提供了classes()和interfaces()方法允许你只浏览超类和接口类。
isArray() 检查某个类型是不是数组,甚至是<? extends A[]>。
getComponentType() 返回组件类型数组。

resolveType

resolveType是一个可以用来“替代”context token(译者:不知道怎么翻译,只好去stackoverflow去问了)中的类型参数的一个强大而复杂的查询操作。例如,

TypeToken<Function<Integer, String>> funToken = new TypeToken<Function<Integer, String>>() {};

TypeToken<?> funResultToken = funToken.resolveType(Function.class.getTypeParameters()[1]));
// returns a TypeToken<String>

TypeToken将Java提供的TypeVariables和context token中的类型变量统一起来。这可以被用来一般性地推断出在一个类型相关方法的返回类型:

TypeToken<Map<String, Integer>> mapToken = new TypeToken<Map<String, Integer>>() {};
TypeToken<?> entrySetToken = mapToken.resolveType(Map.class.getMethod("entrySet").getGenericReturnType());
// returns a TypeToken<Set<Map.Entry<String, Integer>>>

Invokable

Guava的Invokable是对java.lang.reflect.Method和java.lang.reflect.Constructor的流式包装。它简化了常见的反射代码的使用。一些使用例子:

方法是否是public的?

JDK:

Modifier.isPublic(method.getModifiers())

Invokable:

invokable.isPublic()

方法是否是package private?

JDK:

!(Modifier.isPrivate(method.getModifiers()) || Modifier.isPublic(method.getModifiers()))

Invokable:

invokable.isPackagePrivate()

方法是否能够被子类重写?

JDK:

!(Modifier.isFinal(method.getModifiers())
|| Modifiers.isPrivate(method.getModifiers())
|| Modifiers.isStatic(method.getModifiers())
|| Modifiers.isFinal(method.getDeclaringClass().getModifiers()))

Invokable:

invokable.isOverridable()

方法的第一个参数是否被定义了注解@Nullable?

JDK:

for (Annotation annotation : method.getParameterAnnotations[0]) {
if (annotation instanceof Nullable) {
return true;
}
}
return false;

Invokable:

invokable.getParameters().get(0).isAnnotationPresent(Nullable.class)

构造函数和工厂方法如何共享同样的代码?

你是否很想重复自己,因为你的反射代码需要以相同的方式工作在构造函数和工厂方法中?

Invokable提供了一个抽象的概念。下面的代码适合任何一种方法或构造函数:

invokable.isPublic();
invokable.getParameters();
invokable.invoke(object, args);

List的List.get(int)返回类型是什么?
Invokable提供了与众不同的类型解决方案:

Invokable<List<String>, ?> invokable = new TypeToken<List<String>>()        {}.method(getMethod);
invokable.getReturnType(); // String.class

Dynamic Proxies

newProxy()

实用方法Reflection.newProxy(Class, InvocationHandler)是一种更安全,更方便的API,它只有一个单一的接口类型需要被代理来创建Java动态代理时

JDK:

Foo foo = (Foo) Proxy.newProxyInstance(
Foo.class.getClassLoader(),
new Class<?>[] {Foo.class},
invocationHandler);

Guava:

Foo foo = Reflection.newProxy(Foo.class, invocationHandler);

AbstractInvocationHandler

有时候你可能想动态代理能够更直观的支持equals(),hashCode()和toString(),那就是:

  1. 一个代理实例equal另外一个代理实例,只要他们有同样的接口类型和equal的invocation handlers。
  2. 一个代理实例的toString()会被代理到invocation handler的toString(),这样更容易自定义。

AbstractInvocationHandler实现了以上逻辑。

除此之外,AbstractInvocationHandler确保传递给handleInvocation(Object, Method, Object[]))的参数数组永远不会空,从而减少了空指针异常的机会。

ClassPath

严格来讲,Java没有平台无关的方式来浏览类和类资源。不过一定的包或者工程下,还是能够实现的,比方说,去检查某个特定的工程的惯例或者某种一直遵从的约束。

ClassPath是一种实用工具,它提供尽最大努力的类路径扫描。用法很简单:

ClassPath classpath = ClassPath.from(classloader); // scans the class path used by classloader
for (ClassPath.ClassInfo classInfo : classpath.getTopLevelClasses("com.mycomp.mypackage")) {
...
}

在上面的例子中,ClassInfo是被加载类的句柄。它允许程序员去检查类的名字和包的名字,让类直到需要的时候才被加载。

值得注意的是,ClassPath是一个尽力而为的工具。它只扫描jar文件中或者某个文件目录下的class文件。也不能扫描非URLClassLoader的自定义class loader管理的class,所以不要将它用于关键任务生产任务。

Class Loading

工具方法Reflection.initialize(Class…)能够确保特定的类被初始化——执行任何静态初始化。

使用这种方法的是一个代码异味,因为静态伤害系统的可维护性和可测试性。在有些情况下,你别无选择,而与传统的框架,操作间,这一方法有助于保持代码不那么丑。

原创文章,转载请注明: 转载自并发编程网 – ifeve.com本文链接地址: google Guava包的reflection解析

google Guava包的reflection(反射)解析的更多相关文章

  1. com.google.guava 包解析 ——Google Guava官方教程(中文版)

    全网址           http://ifeve.com/google-guava/ 竹子博客:  http://www.cnblogs.com/peida/archive/2013/06/08/ ...

  2. google Guava包的ListenableFuture解析

     一. ListenableFuture是用来增强Future的功能的. 我们知道Future表示一个异步计算任务,当任务完成时可以得到计算结果.如果我们希望一旦计算完成就拿到结果展示给用户或者做另外 ...

  3. Google Guava官方教程(中文版)

    Google Guava官方教程(中文版) 原文链接  译文链接 译者: 沈义扬,罗立树,何一昕,武祖  校对:方腾飞 引言 Guava工程包含了若干被Google的 Java项目广泛依赖 的核心库, ...

  4. Google Guava

    公司用到了 Joiner  HashMultimap 等  都是属于Google Guava包中的东西 官方文档 http://ifeve.com/google-guava/ 有时间了整理一下

  5. Java创建和解析Json数据方法(五)——Google Gson包的使用

    (五)Google Gson包的使用 1.简介 Gson包中,使用最多的是Gson类的toJson()和fromJson()方法:         ①toJson():将java对象转化为json数据 ...

  6. Google Guava Cache 全解析

    Google guava工具类的介绍和使用https://blog.csdn.net/wwwdc1012/article/details/82228458 LoadingCache缓存使用(Loadi ...

  7. Google Guava的5个鲜为人知的特性

    译文出处: 花名有孚   原文出处:takipi.com Google Guava有哪些比较冷门但却又实用的特性呢? 它是最流行的开源库之一,你应该听过它的大名,它诞生的地方正是人们举办真正的魁地奇比 ...

  8. Google Guava 类库简介

    Guava 是一个 Google开发的 基于java的类库集合的扩展项目,包括 collections, caching, primitives support, concurrency librar ...

  9. Google Guava学习笔记——简介

    Google Guava是什么东西?首先要追溯到2007年的“Google Collections Library”项目,它提供对Java 集合操作的工具类.后来Guava被进化为Java程序员开发必 ...

随机推荐

  1. Redis持久化RDB、AOF

    持久化的意思就是保存,保存到硬盘.第一次接触这个词是在几年前学习EF. 为什么要持久化 redis定义:Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代 ...

  2. 剑指offer59:按之字形顺序打印二叉树:[[1], [3,2], [4,5,6,7]]

    1 题目描述 请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推. 2 思路和方法 先给定一个二叉树的样式: ...

  3. 剑指offer25:复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),结果返回复制后复杂链表的head。

    1 题目描述 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head.(注意,输出结果中请不要返回参数中的节点引用 ...

  4. python检测挖矿特征的几种方式

    电脑性能上: ①cpu和内存使用率(常见): python 实时得到cpu和内存的使用情况方法_python_脚本之家https://www.jb51.net/article/141835.htm ② ...

  5. 调用WebService时加入身份验证,以拒绝未授权的访问

    众所周知,WebService是为企业需求提供的在线应用服务,其他公司或应用软件能够通过Internet来访问并使用这项在线服务.但在有些时候的某些应用服务不希望被未授权访问,那么此时我们可以一下几种 ...

  6. 记一次纯sqlite数据库的小项目开发经历

    sqlite有哪些坑 1.支持的数据量级:根据SQLite的官方提示:http://www.sqlite.org/limits.htmlSQLIte数据库最大支持128TiB(140 terabyte ...

  7. Spring Cloud Alibaba学习笔记(5) - 整合Sentinel及Sentinel规则

    整合Sentinel 应用整合Sentinel 在dependencies中添加依赖,即可整合Sentinel <dependency> <groupId>com.alibab ...

  8. [NOIP11.1模拟赛]补番报告

    Preface 昨天开始补某科学的超电磁炮S 感觉今天就好了点,炮姐赛高 T1 一开始一直想欧拉定理&ex欧拉定理,结果估计70分,数组开小了GG,看了正解发现是我学傻了 T2 一看就是数据结 ...

  9. flask/app.py-add_url_rule源码分析

    之前分析route方法的时候,可以看到中间会调用add_url_rule方法,add_url_rule方法和route方法一样属于Flask这个类的. add_url_rule方法主要用来连接url规 ...

  10. ipv4与ipv6 Inet4Address类和Inet6Address类

    在设置本地IP地址的时候,一些人会疑惑IPv4与IPv6的区别是什么?下面由学习啦小编为你分享ipv4与ipv6的区别的相关内容,希望对大家有所帮助. ipv4与ipv6的区别 在windows 7以 ...