• Java基础


    泛型作用

    编译检查 + 强转

    1. 编译检查
    2. 取值时自动转型(其实作用1是为作用2进行铺垫)

    各种复杂的泛型其实都是为了实现这两点,即保证:

    1. 我们存进去的类型确实我们需要存进去的
    2. 这样我们取出来的类型就也一定不会有错,所以编译器就可以帮助我们大胆的进行强转

    一个小栗子

    例如现在有这样的一个需求,我想要实现一个加法器:

    • 可以计算整数之和(只考虑int)
    • 可以计算小数之和(只考虑double)

    假设我们还活在没有泛型的远古时代

    1. 首先定义一个接口
    inteface Calculator {
        Object add(Object num1,Object num2);
    }
    
    1. 接着用整数的方式来实现
    public class IntegerCal implements Calculator {
        @Override
        public Object add(Object num1, Object num2) {
            return (Integer) num1 + (Integer) num2;
        }
    }
    
    1. 先别急着写小数的,我们先来测试下这个整数加法器
    public class CalTest {
        public static void main(String[] args) {
            Integer intRes = (Integer)new IntegerCal().add(1,2);
            System.out.println(intRes);
        }
    }
    

    似乎很完美,但是加入我们没按规矩,把小数当了参数呢?

    Integer intRes = (Integer)new IntegerCal().add(1.1,2);
    

    在运行时就会报一个类型转换的错误了,为了保证错误的友好型,我们决定在整数加法器中进行一些校验

    1. 加点类型校验
    public class IntegerCal implements Calculator {
        @Override
        public Object add(Object num1, Object num2) {
            if(!(num1 instanceof Integer) ) {
                throw new RuntimeException(num1 + "不是整数");
            }
            if(!(num2 instanceof Integer) ) {
                throw new RuntimeException(num2 + "不是整数");
            }
            return (Integer) num1 + (Integer) num2;
        }
    }
    
    1. 现在你准备好写小数加法器了吗?如果我告诉你这只是暂时的需求,未来还可能加入乘法,除法等方法呢?
      你说聪明如我,把这些校验逻辑抽成一个方法不就行了?
      那么如果我说后面还要支持字符串加法器,Person类加法器,Student类加法器,各种各样类型的加法器的时候又该如何呢?
      还有一个致命的缺点,这些错误你不把程序跑起来是发现不了的

    升级后的栗子

    • 升级后的泛型接口
    public interface CalculatorGeneric<T> {
        T add(T num1,T num2);
    }
    
    • 升级后的整数加法器
    public class IntegerCalGeneric implements CalculatorGeneric<Integer> {
    
        @Override
        public Integer add(Integer num1, Integer num2) {
            return num1 + num2;
        }
    }
    
    • 使用全新的泛型加法器
    public class CalTest {
        public static void main(String[] args) {
            // 不需要我们自己强转了
            Integer intRes = new IntegerCalGeneric().add(1,2);
            System.out.println(intRes);
        }
    }
    

    又有人不老实想传小数怎么办?编译时就给你打趴下

    public class CalTest {
        public static void main(String[] args) {
            // 想蒙混过关?编译都不让你过
            Integer intRes = new IntegerCalGeneric().add(1,2);
            System.out.println(intRes);
        }
    }
    

    小结

    上面这个栗子就很好的体现出了泛型的作用:
    编译检查 + 强转
    正因为编译时保证了我传进去的都是整数,所以我才可以放心大胆的在返回时进行强转

    泛型与反射

    泛型还有一个非常大的作用,就是在编译时,如果类的参数类型就可以确定了,泛型是可以被反射读到的。

    例如下面这种情况

    class StartListener extends AppListener<StartEvent>
    
    class UserMapper extends BaseMapper<User>
    
    • 这个类的类型参数在编译的时候我就可以确定了,所以虽然说泛型被擦除了,但是最后在反射中是可以读取到它的信息的,JVM通过另外的方式存储了它的信息
    • 通过这个方法我们相当于在运行时拥有了一个参数化的类型,并且这个参数类型具体是什么我们还知道,我们就可以利用这个特性做很多事情

    应用

    • Spring中的监听器机制就是利用这个原理实现的,通过泛型声明事件类型,从而拿到监听器想要监听的事件
    • Mybatis Plus中也是通过这种方式拿到UserMapper想要操作的表对象User

    如何拿到?

    关键就在于一个叫做ParameterizedType的类型
    如何拿到呢?
    可以通过Class类中的getGenericInterfaces或者getGenericSuperclass拿到。
    顾名思义,就是拿到泛型的接口和泛型的父类,然后强转成ParameterizedType
    例如下面这个例子:

    public class UserMapper extends BaseMapper<User> {
        public static void main(String[] args) {
            // 第一步:拿到ParameterizedType类型
            ParameterizedType parameterizedType =
                    (ParameterizedType) UserMapper.class
                            .getGenericSuperclass();
    
                            
            // 第二步:拿到真实的参数类型
            Class<User> clazz = (Class<User>) parameterizedType
                    .getActualTypeArguments()[0];
            System.out.println(clazz);
        }
    }
    

    几个术语

    • ArrayList中的E称为类型参数变量 TypeVariable(暂时还没发现有啥实际用途)
    • ArrayList中的Integer称为实际类型参数
    • 整个ArrayList成为泛型类型
    • 整个ArrayList称为参数化的类型ParameterizedType
  • 相关阅读:
    【spring cloud】spring cloud zuul 路由网关
    【IDEA】【maven】idea使用maven插件 打包提示找不到符号找不到类,但是却没有错误
    【java】关于Map的排序性的一次使用,有序的Map
    【idea】idea重新打包依赖了父级项目的子级项目,父级项目代码改变,但是子级项目打包依旧是老的代码 问题解决
    【mysql】在mysql中更新字段的部分值,更新某个字符串字段的部分内容
    CentOS RabbitMQ 高可用(Mirrored)
    CentOS MongoDB 高可用实战
    CentOS 安装 gitlab
    SpringBoot打jar包问题
    java中由类名和方法名字符串实现其调用【反射机制】
  • 原文地址:https://www.cnblogs.com/bax-life/p/14590413.html
Copyright © 2020-2023  润新知