FastJson中如何传递泛型参数以及关于泛型擦除的一点思考

2024-05-27 05:58

本文主要是介绍FastJson中如何传递泛型参数以及关于泛型擦除的一点思考,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、问题说明

现在需要将json字符串转换成带泛型的javaBean(如:List<Student>,List<String>等)

里面的泛型参数不确定,能否将Student、String这些作为参数传到方法里来。

换句话说,能否就传递Student.class、String.class,让fastjson把json字符串转换成相应的对象

public class Student {private String sid; // 学生idprivate String sname; // 学生姓名private int sage; // 学生年龄// 省略构造方法、setter和getter方法
}

代码举例:

如果转换成List<String>,就写成

String listJsonData1 = "[\"张三\",\"李四\",\"王五\"]";
List<String> list1 = JSON.parseObject(listJsonData1, new TypeReference<List<String>>(){});
System.out.println(list1);

如果转换成List<String>,就写成

String listJsonData2 = "[{\"sname\":\"张三\",\"sage\":10,\"sid\":\"1\"},{\"sname\":\"李四\",\"sage\":20,\"sid\":\"2\"}]";
List<Student> list2 = JSON.parseObject(listJsonData2, new TypeReference<List<Student>>(){});
System.out.println(list2);

但是能不能提取成一个方法,只要传入类型形参和json字符串呢?

二、解决方法

直接上解决方案,然后再做说明

public class FastJsonUtil {/*** 把json字符串转换成带泛型的javaBean* @param jsonStr json字符串* @param actualArguments 类型参数* @param rawType 声明泛型的类* @param <T>* @return*/public static <T> T convertToBean(String jsonStr, Type[] actualArguments, Type rawType) {Type type = new ParameterizedTypeImpl(actualArguments, null, rawType);return JSON.parseObject(jsonStr, type);}
}

如何调用呢,上测试方法:

@Test
public void test() {// 转换成List<String>String listJsonData1 = "[\"张三\",\"李四\",\"王五\"]";List<String> list1 = FastJsonUtil.convertToBean(listJsonData1, new Type[]{String.class}, List.class);System.out.println(list1);// 转换成List<String>String listJsonData2 = "[{\"sname\":\"张三\",\"sage\":10,\"sid\":\"1\"},{\"sname\":\"李四\",\"sage\":20,\"sid\":\"2\"}]";List<Student> list2 = FastJsonUtil.convertToBean(listJsonData2, new Type[]{Student.class}, List.class);System.out.println(list2);
}

那如果是其他形式的泛型类呢?比如:Map<Integer,Student>

我们知道,如果直接用TypeReference的话,写法如下:

String mapJsonData = "{\"1\":{\"sname\":\"张三\",\"sage\":10,\"sid\":\"1\"},\"2\":{\"sname\":\"李四\",\"sage\":20,\"sid\":\"2\"}}";
Map<Integer, Student> map = JSON.parseObject(mapJsonData, new TypeReference<Map<Integer, Student>>(){});
System.out.println(map);

那如果用上面封装的工具方法呢?写法如下:

@Test
public void test2() {// 转换成Map<Integer, Student>String mapJsonData = "{\"1\":{\"sname\":\"张三\",\"sage\":10,\"sid\":\"1\"},\"2\":{\"sname\":\"李四\",\"sage\":20,\"sid\":\"2\"}}";Map<Integer, Student> map = FastJsonUtil.convertToBean(mapJsonData, new Type[]{Integer.class, Student.class}, Map.class);System.out.println(map);
}

三、解释说明

有关泛型的一些相对冷门的知识,可以参看https://www.jianshu.com/p/e8eeff12c306(mybatis框架源码中大量使用反射方法,其实通过反射获取泛型,里面就用得很多)

比如:

我和很多人一样,刚开始学java泛型的时候听说过“泛型擦除”。但是到了fastjson上,竟然并没有被擦除,这点让我一直非常疑惑。

先上个例子:

@Test
public void test3() {List<String> list1 = new ArrayList<String>();List<Integer> list2 = new ArrayList<Integer>();System.out.println(list1.getClass() == list2.getClass());
}

打印的结果为true(通过该类本身的构造器生成实例,并且不是生成匿名子类的情况下,泛型是会被擦除的)

我们再仔细看下fastjson里的调用,比如转换成List<String>的那段代码

String listJsonData1 = "[\"张三\",\"李四\",\"王五\"]";
List<String> list1 = JSON.parseObject(listJsonData1, new TypeReference<List<String>>(){});
System.out.println(list1);

可以发现,parseObject传的第二个参数最后面有个大括号,这是什么意思呢?这样传参其实传的是TypeReference的匿名子类(https://www.iteye.com/problems/91634),这个TypeReference本身没有太多特别之处(https://www.jianshu.com/p/0dc13273e931),从这个写法上来看,我们可以自己把test3稍加改动下,就得到不一样的效果,同时还能打印出类型形参对应的class

@Test
public void test4() {List<String> list1 = new ArrayList<String>(){};List<Integer> list2 = new ArrayList<Integer>(){};// 判断两个子类的类型是否相同(false)System.out.println(list1.getClass() == list2.getClass());// 判断list1和list2是否为List的子类或子接口(都为true)System.out.println(List.class.isAssignableFrom(list1.getClass()));System.out.println(List.class.isAssignableFrom(list2.getClass()));// 获取父类声明的类型(java.util.ArrayList<java.lang.String>和java.util.ArrayList<java.lang.Integer>)System.out.println(list1.getClass().getGenericSuperclass());System.out.println(list2.getClass().getGenericSuperclass());// 获取声明的类型参数(class java.lang.String和class java.lang.Integer)ParameterizedType type1 = (ParameterizedType) list1.getClass().getGenericSuperclass();ParameterizedType type2 = (ParameterizedType) list2.getClass().getGenericSuperclass();System.out.println(type1.getActualTypeArguments()[0]);System.out.println(type2.getActualTypeArguments()[0]);
}

以List<String> list1 = new ArrayList<String>(){};为例

现在可以得出一个结论,【泛型不会被擦除的情形之一】如果一个类实例(list1)是声明的泛型类(ArrayList<String>)的子类时,那么这个类实例(list1)的泛型不会被擦除

还有别的情况吗?再举个例子

public class TypeParamStudy {@Testpublic void testGetActualTypeArguments() throws NoSuchFieldException {Field fieldMap = ParameterizedTypeTest.class.getDeclaredField("map");Type typeMap = fieldMap.getGenericType();ParameterizedType parameterizedTypeMap = (ParameterizedType) typeMap;// 获取泛型中的实际类型(class java.lang.String,class java.lang.Integer)Type[] types = parameterizedTypeMap.getActualTypeArguments();System.out.println(types[0]);System.out.println(types[1]);}
}class ParameterizedTypeTest<T> {private Map<String, Integer> map = null;
}

【泛型不会被擦除的情形之二】(以testGetActualTypeArguments单元测试为例)如果该泛型类(Map<String, Integer>)作为另外一个类(ParameterizedTypeTest)的属性,那么该属性的泛型(String, Integer)不会被擦除

以上纯属个人理解,如有错误的地方,还望多多指正,谢谢。

参考链接:

https://www.jianshu.com/p/e8eeff12c306

https://www.iteye.com/problems/91634

https://www.jianshu.com/p/0dc13273e931

附测试代码:

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.kittycoder.po.Student;
import org.junit.Test;import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;/*** Created by luoxiaoshu on 2019-5-18 上午 0:29*/
public class FastJsonUtilTest {/*** 不使用convertToBean时的常规写法*/@Testpublic void test1() {String listJsonData1 = "[\"张三\",\"李四\",\"王五\"]";List<String> list1 = JSON.parseObject(listJsonData1, new TypeReference<List<String>>(){});System.out.println(list1);String listJsonData2 = "[{\"sname\":\"张三\",\"sage\":10,\"sid\":\"1\"},{\"sname\":\"李四\",\"sage\":20,\"sid\":\"2\"}]";List<Student> list2 = JSON.parseObject(listJsonData2, new TypeReference<List<Student>>(){});System.out.println(list2);// 转换成Map<Integer, Student>String mapJsonData = "{\"1\":{\"sname\":\"张三\",\"sage\":10,\"sid\":\"1\"},\"2\":{\"sname\":\"李四\",\"sage\":20,\"sid\":\"2\"}}";Map<Integer, Student> map = JSON.parseObject(mapJsonData, new TypeReference<Map<Integer, Student>>(){});System.out.println(map);}/*** 测试convertToBean工具方法*/@Testpublic void convertToBean() {// 转换成List<String>String listJsonData1 = "[\"张三\",\"李四\",\"王五\"]";List<String> list1 = FastJsonUtil.convertToBean(listJsonData1, new Type[]{String.class}, List.class);System.out.println(list1);// 转换成List<String>String listJsonData2 = "[{\"sname\":\"张三\",\"sage\":10,\"sid\":\"1\"},{\"sname\":\"李四\",\"sage\":20,\"sid\":\"2\"}]";List<Student> list2 = FastJsonUtil.convertToBean(listJsonData2, new Type[]{Student.class}, List.class);System.out.println(list2);// 转换成Map<Integer, Student>String mapJsonData = "{\"1\":{\"sname\":\"张三\",\"sage\":10,\"sid\":\"1\"},\"2\":{\"sname\":\"李四\",\"sage\":20,\"sid\":\"2\"}}";Map<Integer, Student> map = FastJsonUtil.convertToBean(mapJsonData, new Type[]{Integer.class, Student.class}, Map.class);System.out.println(map);}// 泛型擦除@Testpublic void test3() {List<String> list1 = new ArrayList<String>();List<Integer> list2 = new ArrayList<Integer>();System.out.println(list1.getClass() == list2.getClass());}// 泛型未被擦除// 【泛型不会被擦除的情形之一】如果一个类实例(list1)是声明的泛型类(ArrayList<String>)的子类时,// 那么这个类实例(list1)的泛型不会被擦除@Testpublic void test4() {List<String> list1 = new ArrayList<String>(){};List<Integer> list2 = new ArrayList<Integer>(){};// 判断两个子类的类型是否相同(false)System.out.println(list1.getClass() == list2.getClass());// 判断list1和list2是否为List的子类或子接口(都为true)System.out.println(List.class.isAssignableFrom(list1.getClass()));System.out.println(List.class.isAssignableFrom(list2.getClass()));// 获取父类声明的类型(java.util.ArrayList<java.lang.String>和java.util.ArrayList<java.lang.Integer>)System.out.println(list1.getClass().getGenericSuperclass());System.out.println(list2.getClass().getGenericSuperclass());// 获取声明的类型参数(class java.lang.String和class java.lang.Integer)ParameterizedType type1 = (ParameterizedType) list1.getClass().getGenericSuperclass();ParameterizedType type2 = (ParameterizedType) list2.getClass().getGenericSuperclass();System.out.println(type1.getActualTypeArguments()[0]);System.out.println(type2.getActualTypeArguments()[0]);}
}

 

20190702更新:

test2中转换成Map<Integer, Student>,还可以写成(见下面的写法二)

@Test
public void test2() {// 转换成Map<Integer, Student>// 写法一:String mapJsonData = "{\"1\":{\"sname\":\"张三\",\"sage\":10,\"sid\":\"1\"},\"2\":{\"sname\":\"李四\",\"sage\":20,\"sid\":\"2\"}}";Map<Integer, Student> map = FastJsonUtil.convertToBean(mapJsonData, new Type[]{Integer.class, Student.class}, Map.class);System.out.println(map);// 写法二:(可以稍微作下封装)Map<Integer, Student> map2 = JSON.parseObject(mapJsonData, new HashMap<Integer, Student>(){}.getClass().getGenericSuperclass());System.out.println(map2);
}

 

这篇关于FastJson中如何传递泛型参数以及关于泛型擦除的一点思考的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1006662

相关文章

Andrej Karpathy最新采访:认知核心模型10亿参数就够了,AI会打破教育不公的僵局

夕小瑶科技说 原创  作者 | 海野 AI圈子的红人,AI大神Andrej Karpathy,曾是OpenAI联合创始人之一,特斯拉AI总监。上一次的动态是官宣创办一家名为 Eureka Labs 的人工智能+教育公司 ,宣布将长期致力于AI原生教育。 近日,Andrej Karpathy接受了No Priors(投资博客)的采访,与硅谷知名投资人 Sara Guo 和 Elad G

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

如何在页面调用utility bar并传递参数至lwc组件

1.在app的utility item中添加lwc组件: 2.调用utility bar api的方式有两种: 方法一,通过lwc调用: import {LightningElement,api ,wire } from 'lwc';import { publish, MessageContext } from 'lightning/messageService';import Ca

4B参数秒杀GPT-3.5:MiniCPM 3.0惊艳登场!

​ 面壁智能 在 AI 的世界里,总有那么几个时刻让人惊叹不已。面壁智能推出的 MiniCPM 3.0,这个仅有4B参数的"小钢炮",正在以惊人的实力挑战着 GPT-3.5 这个曾经的AI巨人。 MiniCPM 3.0 MiniCPM 3.0 MiniCPM 3.0 目前的主要功能有: 长上下文功能:原生支持 32k 上下文长度,性能完美。我们引入了

【编程底层思考】垃圾收集机制,GC算法,垃圾收集器类型概述

Java的垃圾收集(Garbage Collection,GC)机制是Java语言的一大特色,它负责自动管理内存的回收,释放不再使用的对象所占用的内存。以下是对Java垃圾收集机制的详细介绍: 一、垃圾收集机制概述: 对象存活判断:垃圾收集器定期检查堆内存中的对象,判断哪些对象是“垃圾”,即不再被任何引用链直接或间接引用的对象。内存回收:将判断为垃圾的对象占用的内存进行回收,以便重新使用。

AI(文生语音)-TTS 技术线路探索学习:从拼接式参数化方法到Tacotron端到端输出

AI(文生语音)-TTS 技术线路探索学习:从拼接式参数化方法到Tacotron端到端输出 在数字化时代,文本到语音(Text-to-Speech, TTS)技术已成为人机交互的关键桥梁,无论是为视障人士提供辅助阅读,还是为智能助手注入声音的灵魂,TTS 技术都扮演着至关重要的角色。从最初的拼接式方法到参数化技术,再到现今的深度学习解决方案,TTS 技术经历了一段长足的进步。这篇文章将带您穿越时

如何确定 Go 语言中 HTTP 连接池的最佳参数?

确定 Go 语言中 HTTP 连接池的最佳参数可以通过以下几种方式: 一、分析应用场景和需求 并发请求量: 确定应用程序在特定时间段内可能同时发起的 HTTP 请求数量。如果并发请求量很高,需要设置较大的连接池参数以满足需求。例如,对于一个高并发的 Web 服务,可能同时有数百个请求在处理,此时需要较大的连接池大小。可以通过压力测试工具模拟高并发场景,观察系统在不同并发请求下的性能表现,从而

多路转接之select(fd_set介绍,参数详细介绍),实现非阻塞式网络通信

目录 多路转接之select 引入 介绍 fd_set 函数原型 nfds readfds / writefds / exceptfds readfds  总结  fd_set操作接口  timeout timevalue 结构体 传入值 返回值 代码 注意点 -- 调用函数 select的参数填充  获取新连接 注意点 -- 通信时的调用函数 添加新fd到

JAVA基础:值传递和址传递

1 值传递和址传递 值传递 方法调用时,传递的实参是一个基本类型的数据 形参改变,实参不变 public static void doSum(int num1,int num2){}main(){doSum(10,20);int i = 10 ;int j = 20 ;doSum(i,j) ;}   public static void t1(int num){num = 20

struts2中的json返回指定的多个参数

要返回指定的多个参数,就必须在struts.xml中的配置如下: <action name="goodsType_*" class="goodsTypeAction" method="{1}"> <!-- 查询商品类别信息==分页 --> <result type="json" name="goodsType_findPgae"> <!--在这一行进行指定,其中lis是一个List集合,但