java泛型总结学习

来源:互联网 发布:淘粉吧登陆淘宝安全 编辑:程序博客网 时间:2024/06/13 06:40

什么是泛型?

  • 泛型 , 即 “参数化类型” . 提到参数 , 最熟悉的就是定义方法时有形参 , 然后调用方法传递实参 . 那么参数化类型如何理解? 就是将类型由原来具体的类型参数化 , 类似于方法中的变量参数 , 此时类型也定义成参数形式 , 然后在调用的时候传入具体的类型 .
    下面采用泛型后, 当通过List<’String> , 就相当于给List传递了参数类型是String, 限定了集合中只能含有String类型元素 , 集合保存了这个类型, 当add(“123”) , 无需进行类型转换 , 因为编译器明确可以传递String类型 . 当然如果传入Integer类型是不会通过编译的 .
    这里写图片描述
  • java中一种类型 , 被参数化的类型 , 也就是可以通过参数传递类型 . 带参数的类型就是泛型 , 被参数化的类型 .

泛型擦除:

java泛型概念的提出 , 导致泛型只是作用于代码的编译阶段 , 在编译过程中 , 对于正确的检验泛型结果后 , 会将泛型的相关信息擦除 , 也就是说 , 成功编译过后的class文件是不包含任何的泛型信息的 . 泛型信息不会进入运行时阶段 .
泛型其实只是在编译器实现的,而虚拟机并不认识泛型类型,所以要在虚拟机中将泛型类型进行擦除 , 也就是说编译阶段使用泛型 , 运行阶段取消泛型 ,
擦除是将泛型类型由父类代替的 , 如String变成Object , 其实使用的时候还是带着强制的类型转换 , 只不过这是比较安全的转换 , 因为在编译阶段已经确保数据的一致性 .


验证泛型只在编译器有效:

所有反射操作是在运行期, 既然是true, 说明编译之后, 程序会采用去泛型化措施 ,
编译过程中, 正确检验泛型结果后, 会将泛型的相关信息擦除, 并且在对象进入和离开方法的边界处添加类型检查和类型转换方法, 也就是说成功编译后的class方法是不包含任何泛型信息的

public static void main(String[] args) {        List<String> stringList = new ArrayList<>();        List commonList = new ArrayList();        // 说明运行期字节码文件是相同的        System.out.println(stringList.getClass() == commonList.getClass()); // true    }

利用反射跳过泛型检查:

public static void main(String[] args) {        List<String> list = new ArrayList<>();        list.add("zhangsan");        // 利用泛型进行添加        try{            Class clazz = list.getClass();            Method method = clazz.getMethod("add", Object.class);            // 添加Integer类型数据, 因为反射是在运行期, 说明泛型的检验是在编译期            method.invoke(list, 100);        } catch (Exception e) {            e.printStackTrace();        }        System.out.println(list);    }

泛型通配符:

对于任意(引用)类型 T,ArrayList<’?> 类型是ArrayList<’T> 的超类型(类似原始类型 ArrayList 和根类型 Object)。
但是ArrayList<’Object>并不是ArrayList<’String>的超类型.

这里写图片描述


泛型上下限:

T super Number,那么T就是Number父类级别的类型,
T extends Number 的话,Number是T的父级别类型.
这里写图片描述


应用:

  • 获取下面test方法的形参中泛型的参数类型.
public class Test {    /**     * 三个参数, 只有两个是泛型类型参数     * @param map     * @param list     * @param str     */    public void test(Map<String,Integer> map, List<String> list, String str) {        System.out.println("hello world");    }    public static void main(String[] args) {        try {            Method method = Test.class.getMethod                    ("test", Map.class, List.class, String.class);            // 获取方法的形参            Type[] paramterTypes = method.getGenericParameterTypes();            // 遍历所有类型, 判断是否是泛型的类型            for(Type type : paramterTypes) {                if(type instanceof ParameterizedType) {                    Type[] ts = ((ParameterizedType)type).getActualTypeArguments();                    for(Type t : ts) {                        // 打印结果:String  Integer  String                        System.out.println(((Class)t).getSimpleName());                    }                }            }        }catch(Exception e) {            e.printStackTrace();        }    }}
  • 比如在三大框架整合中, 我们需要将所有增删改查操作方法放在父类中, UserService操作的实体类型是User, DepartService操作类型是Depart . 那么如何让父类BaseService获得到子类操作的实体类型(Hibernate中增删改查需要获取实体类型名称) ?
    父类:
public class BaseService<T> {    private Class<T> clazz;    @SuppressWarnings("unchecked")    public BaseService() {        // 获取子类的class对象        Class c = (Class<T>) this.getClass();        // 获取子类的extend类型        Type type = c.getGenericSuperclass();        // 判断是否是泛型        if(type instanceof ParameterizedType) {            // 获取泛型的参数类型(默认获取第一个)            Type t = ((ParameterizedType)type).getActualTypeArguments()[0];            clazz = (Class<T>) t;            System.out.println(clazz.getSimpleName());        }    }}

UserService:

public class UserService extends BaseService<User> {    public static void main(String[] args) {        new UserService(); // User    }}

DepartService:

public class DepartService extends BaseService<Depart> {    public static void main(String[] args) {        new DepartService(); // Depart    }}

泛型好处 :
(1) 类型安全
通过泛型定义的变量类型限制, 编译器可以很有效的提高java程序的类型安全 .
(2) 消除强制类型转换
消除强制类型转换, 使得代码更加可读, 并减少出错的机会, 所有的强制转换都是自动

0 0
原创粉丝点击