Java通过反射获取方法的返回值的类型以及参数化的实际类型(上篇)

最近在学mybatis的时候,自己根据参考资料自定义了一个简单的mybatis框架,期望能够简单实现对数据库操作的增删查改功能。在自定义mybatis框架的过程中,在使用注解来进行配置sql语句时,就遇到了一些关于获取返回值类型的小问题。比如说方法 publIc User getOne(int id);的返回值类型是User;public List< User > getAll();的返回值类型是List< User >,但是我们在进行mybatis框架书的时候,通过注解配置时,我们获取的返回值类型需要获取的是实体类的全限定类名,也即是我们实际上需要获取的是 List< User >中的实体类User即可。在我们实际的应用中,为了便于代码的通用性以及复用性,我们习惯使用泛型,比如public List< T> getAll()和public Map<T,V> getAll();这两个方法,就是使用了泛型,是代码更加通用。那么我们首先来了解一下泛型的原理:
定义:把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊的类型。
Java泛型设计原则:只要在编译时期没有出现警告,那么运行时期就不会出现ClassCastException异常.
参数化类型:把类型当作是参数—样传递;<数据类型>只能是引用类型。
相关术语:
List< E >中的E称为类型参数变量。
List< Integer >中的Integer称为实际类型参数。
整个称为List< E >泛型类型。
整个List< Integer >称为参数化的类型ParameterizedType。

不过也就是因为使用了泛型,导致我们在通过反射获取返回值了类型的时候会更加麻烦一些。为了更加方便学习通过反射获取返回值类型以及参数化的实际参数类型,我特意写了几个小案例进行测试讲解。
1.首先创建用于测试的各个类,在这些类中我们不需要写额外的代码实现,只是用于测试获取返回值类型即可。

package dao;

import java.util.List;

import entity.User;

/**
 * @author 陌意随影
 TODO :UserDao测试接口
 *2020年7月26日  下午10:58:04
 */
public interface UserDao {
 
/**
 * 获取所有用户
 * @return 返回所有用户的集合
 */
	public List<User> getAll();
/**
 * 通过指定的id获取一个用户对象
 * @param id: 指定的id
 * @return 返回一个User
 */
	public User getOne(int id);

}

package dao;

import java.util.Map;

/**
 * @author 陌意随影
 TODO :学生dao测试类
 *2020年7月26日  下午11:02:10
 */
public interface StudentDao<T,V> {
	/**
	 * 获取所有对象
	 * @return 返回一个map
	 */
	public Map<T,V> getAll();
	/**
	 * 获取一个T对象
	 * @return 返回T
	 */
	public T getT();
	/**
	 * 获取一个V对象
	 * @return 返回V
	 */
	public	V getV();

}

package entity;

/**
 * @author 陌意随影
 TODO :测试实体类
 *2020年7月26日  下午11:05:27
 */
public class Cat {

}

package entity;

/**
 * @author 陌意随影
 TODO :测试实体类
 *2020年7月26日  下午11:05:10
 */
public class Dog {

}

package entity;

/**
 * @author 陌意随影
 TODO :测试用户类
 *2020年7月26日  下午10:59:22
 */
public class User {

}

然后建立相关的测试类:

package test;


import org.junit.jupiter.api.Test;

class StudentDaoTest {

	@Test
	void testGetAll() {
	}

	@Test
	void testGetT() {
	}

	@Test
	void testGetV() {
	}

}

package test;


import org.junit.jupiter.api.Test;

class UserDaoTest {

	@Test
	void testGetAll() {
		
	}

	@Test
	void testGetOne() {
	}

}

这些类的具体目录结构为:
在这里插入图片描述
2.建立测试类进行测试
2.1首先测试UserDao接口

package test;


import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import org.junit.jupiter.api.Test;

class UserDaoTest {

	@Test
	void test() throws Exception {
	 //根据UserDao接口的全限定类名通过反射获取该接口的字节码
	Class<?> userDaoClass = Class.forName("dao.UserDao");
	//获取UserDao所有的方法
	Method[] methods = userDaoClass.getMethods();
	for(Method method: methods) {
		//获取方法的名称
		String methodName = method.getName();
		//判断是否是UserDao中的getAll()或者getOne(int id)方法,
		if(methodName.startsWith("get")) {
			//返回一个Type对象,表示由该方法对象表示的方法的正式返回类型。
			//比如public List<User> getAll();那么返回的是List<User>
		  Type genericReturnType = method.getGenericReturnType();
		  //获取实际返回的参数名
		  String returnTypeName = genericReturnType.getTypeName();
		  System.out.println(methodName+"的返回参数是:"+returnTypeName);
		  //判断是否是参数化类型
		  if(genericReturnType instanceof ParameterizedType) {
			  //如果是参数化类型,则强转
			  ParameterizedType parameterizedType = (ParameterizedType) genericReturnType;
			//获取实际参数类型数组,比如List<User>,则获取的是数组[User],Map<User,String> 则获取的是数组[User,String]
			  Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
			  for(Type type:actualTypeArguments) {
				  //强转
				  Class<?> actualTypeArgument = (Class<?>) type;
				 //获取实际参数的类名
				  String name = actualTypeArgument.getName();
				  System.out.println(methodName+"的返回值类型是参数化类型,其类型为:"+name);
			  }
			  
		  }else {
			  //不是参数化类型,直接获取返回值类型
			  Class<?> returnType = method.getReturnType();
              //获取返回值类型的类名
			  String name = returnType.getName();
			  System.out.println(methodName+"的返回值类型不是参数化类型其类型为:"+name);
			  
		  }
		  
		}
		
		
	}
	}


}

从该测试类中我们可以看到,我们首先获取对应的方法后,使用

//比如public List<User> getAll();那么返回的是List<User>
Type genericReturnType = method.getGenericReturnType();

来获取方法的正式返回类型,比如public List< User > getAll();那么返回的是List< User >,public Map< String,User> getAll();那么返回的是Map< String,User>;,然后判断是否是参数化类型,在这里多说一句,其实参数化类型就是我们常见的List< User >,Map< String,Integer>等这些 < > 中的参数,我们可以通过是否有< >来判断是不是参数化。当我们知道是参数化类型后,需要进行强转:

 ParameterizedType parameterizedType = (ParameterizedType) genericReturnType;

2.2下面我们开始对ParameterizedType进行解析:

public interface ParameterizedType extends Type {
    Type[] getActualTypeArguments();

    Type getRawType();

    Type getOwnerType();
}

2.2.1 Type[] getActualTypeArguments​():返回一个Type对象的数组,表示此类型的实际类型参数。
请注意,在某些情况下,返回的数组为空。 如果此类型表示嵌套在参数化类型中的非参数化类型,则可能会发生这种情况。
结果:一个 Type对象的数组,表示此类型的实际类型参数。
2.2.2 Type getRawType​():返回表示此类型的类或接口的 Type对象。
结果 :表示声明此类型的类或接口的 Type对象 。
2.2.3Type getOwnerType():返回表示此类型Type成员的类型的Type对象。 例如,如果该类型是O< T >.I < S > ,返回的表示O< T > 。
如果此类型是顶级类型,则返回null 。
结果 :表示此类型Type成员的类型的Type对象。 如果此类型是顶级类型,则返回null。
2.3获取返回类型的实际参数数组:

  Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();

2.4将actualTypeArguments 每个Type强转为Class后获取其方法名,也就是获取参数类型的全限定类名,如Map<User,Integer>中的实际参数数组actualTypeArguments ={User,Integer},其全限定类名对应为{dao.User , java.lang.Integer }。

2.5如果不是参数化类型的话,可直接获取返回值类型然后获取返回值类型的全限定类名:

 //不是参数化类型,直接获取返回值类型
			  Class<?> returnType = method.getReturnType();
              //获取返回值类型的类名
			  String name = returnType.getName();

2.6测试的结果为:
在这里插入图片描述
我们发现 public List< User > getAll();的返回值类型是参数化类型,为List< User >,其全限定类名表示为:java.util.List<entity.User>
public User getOne(int id);的返回值类型为User ,不是参数化类型,其全限定类名表示为:entity.User。
由此我们初步懂得了如何获取简单的参数化类型的返回值。那么对于更加复杂的泛型类的如Map<T,V>的实际参数该怎么获取呢?欲知后事如何请看
Java通过反射获方法的返回值的类型以及参数化的实际类型(下篇)

该测试的源代码已经上传到我的个人博客,如有需要移步到我的个人博客里并下拉到文章结束底部点击链接下载源码:http://moyisuiying.com/index.php/javastudy/170.html

  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陌意随影

您的鼓励是我最大的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值