Mybatis学习笔记之泛型介绍及解析

本文介绍了Java泛型的基本概念,包括泛型的引入原因、jdk中与泛型相关的接口,如ParameterizedType、TypeVariable和WildcardType。接着讲解了泛型的解析,特别是在Mybatis框架中的应用,以及解析方法返回值和字段泛型的实例。此外,还讨论了泛型的局限性,强调了泛型带来的编译时检查的好处。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

泛型介绍

我们都知道一个数据类型可以定义很多变量,例如:int a,b,c,d;那有没有一种类型可以定义或代表很多种数据类型呢?这就是泛型出现的原因。泛型简单来说就是数据类型参数化。
java中泛型是在jdk1.5出现的,之所以能出现还要得益于Object对象,因为java中泛型属于伪泛型,采用泛型擦除机制,最终泛型对象都是被Object对象引用。
既然说最终都是Object对象,那为什么还要出现泛型,这不是对此一举吗?因为泛型带来的好处是编译时期检查,可以防止"猫中出现狗"的现象。

jdk中跟泛型有关的接口

type

type是所有类型的父接口,只有一个方法

public interface Type {
	//返回描述这个类型的字符串
    String getTypeName() {
        return toString();
    }
}

它有四个子接口和一个实现类,实现类是Class,子接口是:ParameterizedType,TypeVariable,WildcardType,GenericArrayType。Class类就不详细介绍,下面介绍其四个子接口。

ParameterizedType参数化的类型

ParameterizedType表示的是泛型被指定为具体数据类型的类型,例如: Map<Cat,Dog>,List<Cat>等等
ParameterizedType中的方法:

public interface ParameterizedType extends Type {
	//获取实际的类型参数,例如List<Cat>中的Cat的Class对象
    Type[] getActualTypeArguments();
	//获取原生类型,例如List<Cat>中的List的Class对象
    Type getRawType();
	//获取拥有者类型,指的是这个类或者接口是定义在另一个类或者接口的内部的
	//例如Map中的Entry接口,获取到的类型就是Map的Class对象,如果是顶层类则返回的是null
    Type getOwnerType();
}

TypeVariable类型变量

TypeVariable表示的是原始的泛型信息
TypeVariable中的方法:

public interface TypeVariable<D extends GenericDeclaration> extends Type, AnnotatedElement {

	//获取泛型类型的上边界类型,为什么是数组,因为可以通过 & 指定多个上边界
	//例如List<T extends Number & Serializable>
    Type[] getBounds();

	//获取泛型声明,意思是泛型声明的地方,java中有三个地方可以声明泛型
	//1、类上,例如:List<T>,此方法返回的就是 List的Class对象
	//2、构造器上,一般用不到,没有多大意义,例如:public <T> Cat(T t){},此方法返回的就是 Cat的Constructor对象
	//3、方法上,例如:public <T> void say(T t){},此方法返回的就是Method对象
    D getGenericDeclaration();

	//返回泛型的名称,例如List<T>中的T
    String getName();

	//这个方法是jdk1.8出来的,用于获取上边界的注解类型,注解可以声明在泛型的边界上
	//举个例子,有注解@Beauty, List<T extends @Beauty Number>
    AnnotatedType[] getAnnotatedBounds();
}

WildcardType 通配符类型

通配符一般用于方法参数的位置, 例如 public void process(List<? extend number>){}
WildcardType 中的方法:

public interface WildcardType extends Type {
	//获取上边界,可以通过 & 指定多个上边界
    Type[] getUpperBounds();

   //获取下边界,可以通过 & 指定多个下边界
    Type[] getLowerBounds();
  }

解析泛型

泛型解析主要是框架或者是类库的开发者而言的,对于我们这些普通的开发者来说基本用不到泛型解析。为什么要对泛型进行解析呢?因为框架开发者并不知道我们所使用的类是什么,又要使框架能正确的处理我们写的类。
举个例子,

public interface UserMapper{
	@Select("select * from tb_user where username = "#{name}"")
	List<User> selectUserByName(@Param("name")String name)
}

没有使用xml指定返回值类型是User对象,那么Mybatis如何知道把结果集封装到User对象中的呢?

//通用解析方法,可以根据实际情况做修改
public List<Class<?>> getActualGenericClass(Type type) {
        //不存在泛型
        if (type instanceof Class) {
            return Collections.singletonList((Class<?>) type);
        }
        //存在泛型
        if (type instanceof ParameterizedType) {
            List<Class<?>> list = new ArrayList<>();
            ParameterizedType parameterizedType = (ParameterizedType) type;
            //获取泛型实际的参数
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                //如果有一个未指定泛型类型则抛出异常,例如:Map<String,T>,此时T未指定具体类型
                if (!(actualTypeArgument instanceof Class)) {
                    throw new IllegalArgumentException();
                }
                list.add((Class<?>) actualTypeArgument);
            }
            return list;
        }
        //如果不是ParameterizedType,则说返回值是类型变量,
        // 例如,public <T> T get(){} 中的返回值T
        // 抛出异常
        throw new IllegalArgumentException();
    }

1、解析方法返回值及字段的泛型

//解析字段中的泛型和解析方法返回值的泛型是基本一样的,把Method换成Field,
//method.getGenericReturnType()换成field.getGenericType()即可

//假设拿到了UserMapper中的selectUserByName方法
Method method = getMethod(clazz);
//获取返回值的泛型类型
Type returnType = method.getGenericType();
//list中只有一个元素,为User的Class对象
List<Class<?>> list = getActualGenericClass(returnType);

2、解析类上的泛型

一般解析类上的泛型是借助超类实现的,比如通用Mapper,要继承Mapper接口,并指定泛型类型,还有比如fastjson中的TypeReference对象,传递此参数时要指定其实现子类或者匿名类,并指定泛型类型。
按照通用Mapper方式进行解析:

public interface UserMapper extends Mapper<User> {
}
Class<?> clazz = UserMapper.class;
Type type = clazz.getGenericSuperclass();
//list中只有一个元素,为User的Class对象
List<Class<?>> list = getActualGenericClass(type);

fastjson中,我们想要把json字符转解析成List<User>,可以创建一个TypeReference的子类并指定泛型的类型为List<User>,泛型类型会被解析成ParameteredType,getRawType()方法返回的是List的Class对象,getActualTypeArguments()方法返回的数组只有一个,为User的Class对象。User对象是我们定义的,但是框架通过泛型解析拿到了User对象的Class信息,通过反射就可以创建User对象并进行属性的填充。详细信息参考fastjson的源码。

泛型的局限性

java中的泛型由于是在后期添加的特性,为了向前兼容,不能做到像C++一样完全的泛型,还是有局限性的,例如,不能 new一个泛型或者数组,T t = new T(),不能调用泛型上限中不存在的方法,即使我们知道所指定的泛型对象中有此方法。

public class Animal<T>{
 	//编译报错
 	//T t = new T();
 	//T [] ts = new T[10];
	public void say(T t){
		//编译报错
		t.say();
	}
}

总结

泛型的出现,把很多错误转移到编译时期,还是给开发者带来极大的方便的,不至于等到运行时才发现错误

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值