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

发布于 2020-07-26  1875 次阅读


最近在学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 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中的实际参数数组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
public User getOne(int id);的返回值类型为User ,不是参数化类型,其全限定类名表示为:entity.User。
由此我们初步懂得了如何获取简单的参数化类型的返回值。那么对于更加复杂的泛型类的如Map的实际参数该怎么获取呢?欲知后事如何请看
Java通过反射获方法的返回值的类型以及参数化的实际类型(下篇):

http://moyisuiying.com/index.php/javastudy/152.html

被测试文件的具体代码已经上传到个人博客服务器,有需要的请直接点击下载,解压后直接导入eclipse即可:http://moyisuiying.com/wp-content/uploads/2020/07/ReflectionAcquisitionGenerics.rar


繁华落尽,雪花漫天飞舞。