Java泛型详解

文章介绍了Java中泛型的两个应用场景,一是用于创建通用的工具函数,如从集合中随机抽取元素;二是抽象业务层面上的共同处理流程,例如泛型抽象类。接着解释了泛型的类型擦除概念,即在编译后,泛型信息会消失,但在编译时能确保类型安全。最后讨论了泛型的边界通配符和泛型方法的返回类型转换机制。

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

我为什么要用泛型

当前我有两个场景要用到泛型,而且感觉非常棒!
1、当我写一个工具函数时,所谓工具,肯定就是大家共用的,但使用的对象肯定是不同,不可能每个对象都写个工具函数吧。比如我要从对象集合中随机抽取N个对象,就可以用泛型,一个函数直接搞定。直接上代码:

/**
     * 从集合中随机取出N个不重复的元素
     * @param list 需要被取出数据的集合
     * @param n 取出的元素数量
     * @return
     */
    public static <T> List<T> createRandomList(List<T> list, int n) {
        Map<Integer,String> map = new HashMap();
        List<T> news = new ArrayList();
        if (list.size() <= n) {
            return list;
        } else {
            while (map.size() < n) {
                int random = (int)(Math.random() * list.size());
                if (!map.containsKey(random)) {
                    map.put(random, "");
                    news.add(list.get(random));
                }
            }
            return news;
        }
    }

注意:返回类型前面的不可少,这个T意思是告诉编译器,T是个泛型,有兴趣的可以和下面这个写法的区别

private <T> T getStudent(List<T> list){
	// 如果入参不是List而是Set,那么返回的也将是Set
}

2、将上面的工具场景进行放大到业务层面,比如我给用户推荐数据时,我推荐的数据会有多种不同的来源,但这些数据都要走一套相同的处理流程,那么就可以将这些数据通道抽象出一部分,这个抽象类可以用泛型来接收不同的通道对象。

@Component
public abstract class AbstractModuleService<T> 
{
...
}
@Service
public class StaticModuleServiceImpl extends AbstractModuleService<StaticModule> implements IStaticModuleService
{
...
}

进阶

了解完了基本用法后,看看泛型到底是怎么玩儿的?
首先,大家都知道,我们写的代码是要编译后交给jvm执行的,而虚拟机没有泛型类型的对象。我们来验证下:

1、

ArrayList<String> list1 = new ArrayList<String>();
list1.add("abc");
ArrayList<Integer> list2 = new ArrayList<Integer>();
list2.add(123);
System.out.println(list1.getClass() == list2.getClass());

输出的结果为:true
其实不难理解,大家都是ArrayList,只不过包的东西不一样而已。
2、

ArrayList<Integer> list = new ArrayList<Integer>(); // 定义的Integer类型的
list.add(1);  //这样调用add方法只能存储整形,因为泛型类型的实例为 Integer
list.getClass().getMethod("add", Object.class).invoke(list, "我是字符串了"); // 通过反射,发现竟然能添加String类型的

说明Integer泛型实例在编译之后被擦除了,只保留了原始类型。1

思考

知道了泛型的类型擦除,我们不难分析出,为什么T没有任何方法了,它就是一个虚拟的东西。
那么泛型被擦除了,为何还能返回我想要的类型?
1、因为返回时T会被替换为Object,当你用你想要的类型接收时,Object会强转成接收的类型。
List --> List --> List
List --> List --> List --> List
2、如果T通过继承进行了限定,那么T被擦除就会留下限定类型
public static T fun(T[] t)
类擦除后
public static User fun(User[] t)
3、通配符?和T的区别?
?extend User 上界通配符
?super User 下界通配符
List 集合元素必须是同类型的,例:[1,2,3,4]
List<?> 集合元素可以是任何类型,例:[1,“string”,User]
因此T可以操作,?不可以操作,例:有T t = fun(); 而没有? f = fun();


  1. Java泛型基本上都是在编译器这个层次上实现的,在生成字节码中是不包括泛型中的类型信息,使用泛型的时候添加上类型参数,在编译器编译的时候会去掉,这个过程称为类型擦除。 ↩︎

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Mr Yin

您获益,我得意,您打赏,我敬礼

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

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

打赏作者

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

抵扣说明:

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

余额充值