搜索
首页Javajava教程泛型与反射的使用总结之泛型篇

     在我看来,JDK5.0绝对是一个很具有里程碑意义的版本,在这个版本中,提供了非常多的很有价值的新特性,泛型就是其中之一,并且对反射机制进行了增强,而且5.0版本还把以前集合框架进行了重构全部添加了泛型支持。
    从5.0发布到现在差不多快有10年时间了,关于这方面的知识介绍网上可以查到很多,书上也都有讲到。大象现在再写这些东西,一是将自己的经历体会总结出来作一个积累,另外一点是希望能够给刚接触这方面的童鞋一点帮助。
    泛型最大的好处就是类型检查,尤其是对集合非常有用,另外在底层代码设计中很有用处,它实现了重用的功能。泛型有两种定义方式,一个是泛型类,另一个是泛型方法。
    那到底什么是泛型呢?简单点讲(可能不严谨),就是用到了类型参数这样的类型变量,不管是类、接口还是方法,都可以说是用到了泛型。请看例子:
    泛型类
    public class Person {
        private T t;
        public T getT() {
            return t;
        }
        public void setT(T t) {
            this.t = t;
        }
    }
    T就是类型变量,是一个参数化类型,用尖括号(<>)括起来,放在类名的后面。泛型类的类型变量可以定义多个。类型变量一般都使用一个大写字母表示,比如本例的Person,JDK中的List,Map等等。
    用具体的类替换类型变量就可以实例化泛型类:Person person = new Person();
    像这样实例化是错误的:Person person = new Person(); //ERROR
    泛型方法

public  T get(String key, Object params) {
       return (T) getSqlSession().selectOne(key, params);
    }

    这是我在SSM3示例的MyBatisDao这个类里面定义的一个方法,此方法就是一个泛型方法。就是类型变量,而get前面的T是返回类型。其实这个方法是存在类型安全问题的,如果我在RoleService里面调用这个方法,将返回类型T写成User,编译器是不会有任何警告信息的。
    但如果我改写一下,将MyBatisDao加上泛型,public class MyBatisDao extends SqlSessionDaoSupport
    这时User的返回类型就出现编译错误了:

080742450.jpg

    编译器根据MyBatisDao这个Role类型变量就会推断出它里面定义的get方法应该返回Role类型。不过这样改过之后MyBatisDao就变成泛型类了,而get方法也不再是泛型方法。那么泛型方法能不能有安全检查呢?有,但是需要一些编程技巧,关键还是跟你写的泛型方法有关系,后面提到的类型参数的限定可以对泛型加以约束,解决一些安全检查的问题。
    类型参数的限定
    对于像这样的类型变量所代表的范围有时太大了点,有时不方便使用。比如现在需要实现了java.io.Serializable接口的泛型类,那么这应该如何做呢?JDK那帮专家们为我们设计了一种叫做“有限制的通配符类型”来解决这个问题。一般我们称为上限和下限,他们一般写成下面这样:
    上限:或者
    下限:
    问号(?)叫做无限制的通配符,它可以表示任何类型。有时候使用类型变量不是那么的方便,通配符类型就很好的解决了这个问题。
    的含义是,T为实现了Serializable接口的类,T为绑定类型(Serializable)的子类型,T和绑定类型可以是接口也可以是类。如果想再加个实现了Comparable接口的限定,只需要这样写:这样写有点不严谨,因为Comparable接口是一个泛型接口可以接收泛型参数,现在我们不讨论这么复杂的情况。
    可以这样理解,任何T类型变量的超类型,还包括T本身,因为T可以看成是它本身的一个超类型。
    那为什么说extends是上限,而extends是下限呢?通过前面两个解释就应该可以看出来,extends Serializable或者extends T说明类型变量必须是Serializable的子类和T变量的子类型,这是不是相当于限制了类型变量的上限了?同理就可以理解下限的含义了。
    说了这么多关于上限和下限的东西,那他们到底有什么用?和怎么用呢?简单来讲,extends限定的类型参数可以从泛型对象读取,super限定的类型参数可以向泛型对象写入。这样说可能有些童鞋要晕了,这到底说的神马东西呢?
    让我们换个方式来讲,关于泛型的上限与下限已经总结出来一个公式:PECS
    PECS表示producer-extends,consumer-super
    上面这个就是说如果参数化类型表示一个生产者,就用,如果它表示一个消费者,就用。再结合上面的说明一起来理解,是不是清楚了呢?如果还不是很理解,大象再附上一小段代码来体会下其中的区别。
    public void add(List list){
        for(T t : list){
           add(t);
       }
    }
    public void add(T t){};
    public void add(T t, List list){
        list.add(t);
    }
    泛型的擦除
    泛型主要是在编译期有效,即编译的时候检查类型安全,现在写代码一般都会用Eclipse或IntelliJ,这些集成开发工具都能做到即时编译,哪里有错马上会出现红色的错误标识。因此如果出现类型转换错误,会很明显的看到结果。但是,在程序的运行阶段,JVM是不认识泛型是神马东西的,所有有泛型的类,接口,方法都会被擦除掉泛型,变成原生类型(raw type),即Person变为Person,List list变成List list等等。
    下面就是之前的Person类用javap反编译后的结果,所有的类型变量T都被擦除掉了,因为T是一个无限定类型所以用Object替换。而且add方法中的也被去掉了。
    public class com.bolo.Person extends java.lang.Object{
       private java.lang.Object t;
       public com.bolo.Person();
       public java.lang.Object getT();
       public void setT(java.lang.Object);
       public void add(java.util.List);
       public void add(java.lang.Object);
       public void add(java.lang.Object, java.util.List);
    }
    因此针对泛型擦除这一特点,我们需要注意这样几点:
        1、JVM里面没有泛型,只有普通的类和方法。
        2、所有的类型参数都用限定类型或者无限定类型用Object来替代。
        3、请谨慎处理方法重载,错误的使用重载不会实现想要的多态。
        4、为保证类型安全,必要时请使用强制类型转换。
    泛型的限制
    基本类型不能作为类型参数。Person就是错误的,只能用Person
    类型检查只能用原始类型。if(t instanceof Person) 如果写成if(t instanceof Person)马上会出现编译错误
    不能实例化类型变量。这样写是错误的:T t = new T()
    不能实例化参数化类型的数组。Person[] p = new Person[5] //ERROR
    不能定义静态实例变量和静态方法。如果你想这样写:private static T a 那对不起,编译器马上会给你一个错误提示。
    其实关于泛型的限制完全可以不用讲,现在编译器都很强大,只要你这样做了,马上会给你显示一个错误。
    最后说下泛型对于集合的用处来说是最大的,集合是一个容器,有了泛型就更方便重用。而我们使用最频繁的集合就是List列表,还有一个容器就是数组,大象在这里强烈建议大家多用List,尽量或最好不要用数组。其一是List有类型安全性检查,其二是数组的功能List都提供了并且更丰富,其三List对gc进行了优化。如果使用数组,特别是操作对象数组,如果经验不足,没有释放数组里面的对象引用,则很容易造成内存泄漏的问题。



更多泛型与反射的使用总结之泛型篇相关文章请关注PHP中文网!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
如何将Maven或Gradle用于高级Java项目管理,构建自动化和依赖性解决方案?如何将Maven或Gradle用于高级Java项目管理,构建自动化和依赖性解决方案?Mar 17, 2025 pm 05:46 PM

本文讨论了使用Maven和Gradle进行Java项目管理,构建自动化和依赖性解决方案,以比较其方法和优化策略。

如何使用适当的版本控制和依赖项管理创建和使用自定义Java库(JAR文件)?如何使用适当的版本控制和依赖项管理创建和使用自定义Java库(JAR文件)?Mar 17, 2025 pm 05:45 PM

本文使用Maven和Gradle之类的工具讨论了具有适当的版本控制和依赖关系管理的自定义Java库(JAR文件)的创建和使用。

如何使用咖啡因或Guava Cache等库在Java应用程序中实现多层缓存?如何使用咖啡因或Guava Cache等库在Java应用程序中实现多层缓存?Mar 17, 2025 pm 05:44 PM

本文讨论了使用咖啡因和Guava缓存在Java中实施多层缓存以提高应用程序性能。它涵盖设置,集成和绩效优势,以及配置和驱逐政策管理最佳PRA

如何将JPA(Java持久性API)用于具有高级功能(例如缓存和懒惰加载)的对象相关映射?如何将JPA(Java持久性API)用于具有高级功能(例如缓存和懒惰加载)的对象相关映射?Mar 17, 2025 pm 05:43 PM

本文讨论了使用JPA进行对象相关映射,并具有高级功能,例如缓存和懒惰加载。它涵盖了设置,实体映射和优化性能的最佳实践,同时突出潜在的陷阱。[159个字符]

Java的类负载机制如何起作用,包括不同的类载荷及其委托模型?Java的类负载机制如何起作用,包括不同的类载荷及其委托模型?Mar 17, 2025 pm 05:35 PM

Java的类上载涉及使用带有引导,扩展程序和应用程序类负载器的分层系统加载,链接和初始化类。父代授权模型确保首先加载核心类别,从而影响自定义类LOA

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
4 周前By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
1 个月前By尊渡假赌尊渡假赌尊渡假赌

热工具

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

适用于 Eclipse 的 SAP NetWeaver 服务器适配器

适用于 Eclipse 的 SAP NetWeaver 服务器适配器

将Eclipse与SAP NetWeaver应用服务器集成。

EditPlus 中文破解版

EditPlus 中文破解版

体积小,语法高亮,不支持代码提示功能

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一个PHP/MySQL的Web应用程序,非常容易受到攻击。它的主要目标是成为安全专业人员在合法环境中测试自己的技能和工具的辅助工具,帮助Web开发人员更好地理解保护Web应用程序的过程,并帮助教师/学生在课堂环境中教授/学习Web应用程序安全。DVWA的目标是通过简单直接的界面练习一些最常见的Web漏洞,难度各不相同。请注意,该软件中