译者:万天慧(武祖)

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

举个例子:

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与类Guice的TypeLiteral很相似,但是有一个点特别不同:它能够支持非具体化的类型,例如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. 【转帖】K8S Deployment 命令

    K8S Deployment 命令 https://www.cnblogs.com/Tempted/p/7831604.html 今天学习了一下 kubectl scale deployment xx ...

  2. zblog安装环境介绍?zblog安装需要什么环境

    最近在群里看到很的多人有在问:“安装zblog需要什么环境?”,其实这个问题在zblog官网的程序下载页面有说明,但是不太详细,那么本文的目的就是来给大家介绍下zblog安装环境详细说明. zblog ...

  3. python 之 数据库(内置函数、流程控制、索引)

    10.17 内置函数 强调:mysql内置的函数只能在sql语句中使用 #数学函数 round(x,y) #返回参数x的四舍五入的有y位小数的值 rand() #返回0到1内的随机值,可以通过提供一个 ...

  4. Qt:用 __thread 关键字让每个线程有自己的全局变量

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/wsj18808050/article/d ...

  5. SOS dp

    设$ans=\sum\limits_{A \cap B=\varnothing} f(A)g(B) $ 直接暴力枚举子集是$O(3^n)$, 一个技巧是先预处理出$h(S)=\sum\limits_{ ...

  6. hdu 3265 第一类斯特林数

    先和第二类做一个对比 第一类Stirling数是有正负的,其绝对值是包含n个元素的集合分作k个环排列的方法数目.递推公式为, S(n,0) = 0, S(1,1) = 1. S(n+1,k) = S( ...

  7. NRF52832 Mesh调试,使其同时支持串口打印和RTT打印

    查看开发环境里面,是否有这个文件,如果没有你的话,则添加文件. 然后要在sdk_config.h中添加使能 然后打开刚才添加的文件retarget.c,主意里面这些地方 这里它进行判断,要么使用RTT ...

  8. 解决:error LNK1169: 找到一个或多个多重定义的符号

    每一个c++项目中可以包含多个cpp文件和.h文件,不过只能有而且必须有一个cpp文件中包含main函数,否则就会报错.所以在一个c++项目中不能单独运行一个cpp文件,只能运行一个项目.如果你想一个 ...

  9. 【转载】 Sqlserver使用Left函数从最左边开始截取固定长度字符串

    在Sqlserver数据库的字符串操作中,截取字符串是最常见的操作,sql server提供了3个常用截取字符串方法,LEFT().RIGHT().SUBSTRING(),如果从第一个字符即最左边位置 ...

  10. 【转载】 C#使用Union方法求两个List集合的并集数据

    在C#语言的编程开发中,有时候需要对List集合数据进行运算,如对两个List集合进行交集运算或者并集运算,其中针对2个List集合的并集运算,可以使用Union方法来快速实现,Union方法的调用格 ...