摘要:回到的第二方法的用法,通过上面的分析,我们可以知道,方法其实也是用来获取泛型的实际类型的,这样就可以将响应反序列化为带泛型的类型了。在很多反序列化的开源组件中,都用了这个原理例如的方法,所以我们会经常见到实例化的时候会多个花括号。
前段日子在使用google-http-client.jar 这个组件做http请求时,发现一件有趣的事情,具体代码如下:
try { HttpTransport transport = new NetHttpTransport.Builder().doNotValidateCertificate().build(); requestFactory = transport.createRequestFactory(new HttpRequestInitializer() { @Override public void initialize(HttpRequest request) { int timeout = 5 * 1000; request.setReadTimeout(timeout); request.setParser(new JsonObjectParser(new JacksonFactory())); request.setThrowExceptionOnExecuteError(false); logger.debug("set timeout = {} milliseconds", timeout); } }); } catch (GeneralSecurityException e) { logger.error("init static members failed:", e); } HttpRequest request = requestFactory.buildPostRequest(new GenericUrl(url), content); HttpResponse response =request.execute(); Bean ret = (Map)response.parseAs(Bean.class); ......
这是一段很简单的http请求的代码,引起我注意的是最后一段代码,并且有个疑问:
为什么HttpResponse.parseAs方法可以通过入参Bean.class就能够将结果装配到Bean类,并返回Bean类型?
事实上,HttpResponse.parseAs有两个同名的重载方法:
publicT parseAs(Class dataClass) throws IOException { if (!hasMessageBody()) { return null; } return request.getParser().parseAndClose(getContent(), getContentCharset(), dataClass); } public Object parseAs(Type dataType) throws IOException { if (!hasMessageBody()) { return null; } return request.getParser().parseAndClose(getContent(), getContentCharset(), dataType); }
两个入参不同,返回的类型也不同,第一个方法可以在编译期返回确切的类型,第二个只能返回Object类型,需要使用者自行强转。那么这两个方法到底有什么区别呢,既然存在肯定是为了解决什么问题吧。我们来看看这两个方法用在哪儿:
1、Bean ret = response.parseAs(Bean.class); 2、Mapret = (Map )response.parseAs(new TypeToken
相信已经有的朋友已经看出来了, 像Map
package org.hxb.spring.generic; import java.lang.reflect.ParameterizedType; import java.util.Arrays; import java.util.Map; import org.junit.Test; public class GenericTest { @Test public void test1() { Bean
输出: [T] [java.util.Map]
有人会问我,为什么Bean
Bean> a = new Bean >(); Bean > a = new Bean >(){};
下面那句话多了一对花括号,相信大家都知道这是什么意思,这样就创建了一个匿名类,
第一种方法显示a的类型是Bean
第一种方法显示a的类型是GenericTest$1
匿名类继承类型Bean
所以getGenericSuperclass方法返回一个ParameterizedType的结果,然后通过ParameterizedType的getActualTypeArguments方法便可以获取实际的类型,实际上用这种方法的话Bean就无需在编译器继承某个父类了,直接在运行时声明一个匿名类即可:
package org.hxb.spring.generic; import java.lang.reflect.ParameterizedType; import java.util.Arrays; import java.util.Map; import org.junit.Test; public class GenericTest { @Test public void test2() { Bean> a = new Bean >() { }; ParameterizedType type = (ParameterizedType) a.getClass().getGenericSuperclass(); if (type.getActualTypeArguments() != null) { System.out.println(Arrays.asList(type.getActualTypeArguments())); } } } class Bean { }
上述代码亦可以输出实际类型。
回到HttpResponse的第二parseAs方法的用法:Map
package org.hxb.spring.generic; import java.lang.reflect.ParameterizedType; import java.util.Map; import org.junit.Test; import com.google.common.reflect.TypeToken; public class GenericTest { @Test public void test2() { Bean> a = new Bean >() { }; ParameterizedType type = (ParameterizedType) a.getClass().getGenericSuperclass(); if (type.getActualTypeArguments() != null) { System.out.println(type.getActualTypeArguments()[0]); } } @Test public void test3() { System.out.println(new TypeToken >() {}.getType()); } } class Bean { }
实际输出:
实验结果和我们猜想的那样,我们再看看TypeToken的无参构造方法,
无参构造方法的访问权限是protected,有人会问了,那我怎么实例化?呵呵,其实作者的意图就是为了确保你不能直接实例化TypeToken,但是我们可以用匿名实现类直接继承TypeToken并实例化(就是多了对花括号{})。
无参构造方法调用了父类的capture(捕获)方法,从截图中可以看到,该方法调用了getGenericSuperclass,返回并且判断父类的类型是不是ParameterizedType,不是的话便抛出异常,是就返回第一个。这也验证了我们的想法,其实parseAs方法就是用了上面的原理。
在很多反序列化的开源组件中,都用了这个原理例如com.fasterxml.jackson.databind.ObjectMapper.ObjectMapper 的readValue方法,所以我们会经常见到实例化的时候会多个花括号。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/68802.html
类型擦除 泛型被引入到Java语言中,以便在编译时提供更严格的类型检查并支持通用编程,为了实现泛型,Java编译器将类型擦除应用于: 如果类型参数是无界的,则用它们的边界或Object替换泛型类型中的所有类型参数,因此,生成的字节码仅包含普通的类、接口和方法。 如有必要,插入类型转换以保持类型安全。 生成桥接方法以保留扩展泛型类型中的多态性。 类型擦除确保不为参数化类型创建新类,因此,泛型不会...
摘要:知识点总结泛型知识点总结泛型泛型泛型就是参数化类型适用于多种数据类型执行相同的代码泛型中的类型在使用时指定泛型归根到底就是模版优点使用泛型时,在实际使用之前类型就已经确定了,不需要强制类型转换。 Java知识点总结(Java泛型) @(Java知识点总结)[Java, Java泛型] [toc] 泛型 泛型就是参数化类型 适用于多种数据类型执行相同的代码 泛型中的类型在使用时指定 泛...
摘要:知识点总结反射反射操作泛型知识点总结反射采用泛型擦除的机制来引入泛型。中的泛型仅仅是给编译器使用的,确保数据的安全性和免去强制类型转换的麻烦。 Java知识点总结(反射-反射操作泛型) @(Java知识点总结)[Java, 反射] Java采用泛型擦除的机制来引入泛型。Java中的泛型仅仅是给编译器javac使用的, 确保数据的安全性和免去强制类型转换的麻烦 。但是,__一旦编译完成,...
阅读 1762·2023-04-26 02:14
阅读 3690·2021-11-23 09:51
阅读 1324·2021-10-13 09:39
阅读 3943·2021-09-24 10:36
阅读 2947·2021-09-22 15:55
阅读 3469·2019-08-30 12:57
阅读 1960·2019-08-29 15:30
阅读 1924·2019-08-29 13:19