首页 > 编程语言 >java泛型

java泛型

时间:2023-06-22 15:14:00浏览次数:52  
标签:java String ArrayList list 类型 add 泛型

泛型原理

什么是泛型&为什么引入泛型

public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add(521);//添加 Integer 类型元素
        list.add("wan");//添加 String 类型元素
        list.add(true);//添加 Boolean 类型元素
        list.add('a');//添加 Character 类型元素
        Object item1 = list.get(0);//只能用 Object 接受元素
        list.forEach(item -> {
            //使用 item,这里的 item 类型是 Object,由于不知道 item 的确切类型,我们需要判断之后强转
            if (item instanceof Integer) {
                //执行业务...
            } else if (item instanceof String) {
                //执行业务...
            } else if (item instanceof Boolean) {
                //执行业务...
            } //继续判断类型...
        });
    }

JDK5 之前没有泛型时,声明的 List 集合默认是可以存储任意类型元素。开发的时候由于不知道集合中元素的确切类型,遍历的时候我们拿到的 item 其实是 Object 类型,如果要使用就必须强转,强转就必须得判断当前元素的具体类型,否则直接使用强转很可能会发生类型转换异常。这样就会让开发很不方便,每次都要额外做判断工作。

有了泛型的指定,我们声明的 list 就只能存储规定类型 String ,当存储其他类型的元素时编辑器就会直接给我们报错(可以在 IDEA 开发环境中看 add 方法提示参数类型就是 String),这样类型不匹配的问题就在编译时候就能检查出来,而不会在运行时才抛出异常。而且当我们进行遍历、获取元素等操作时,get 方法返回值就是 String 类型的。

总结泛型的好处

  • 编译期类型安全
  • 避免了强制类型转换运行时异常
  • 同一个类可以操作多种类型数据,代码复用

泛型类

ArrayList 是最典型的泛型类例子。 ArrayList 在定义的时候指定了一个泛型 ,并且下面的添加元素、获取元素等方法也都是对这个 E 进行操作。也可以在一个类中定义多个泛型参数,比如 HashMap。

public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>

泛型方法

有时候开发中我们会有这样一种需求,根据方法传入的参数类型不同,返回不同的返回值类型。

public static <E, T> List<T> convertListToList(List<E> input, Class<T> clzz) {
        List<T> output = Lists.newArrayList();
        if (CollectionUtils.isNotEmpty(input)) {
            input.forEach(source -> {
                T target = BeanUtils.instantiate(clzz);
                BeanUtils.copyProperties(source, target);
                output.add(target);
            });
        }
        return output;
    }

无界泛型通配符

"?" 代表不确定的类型。泛型的上界下界:

IPage<? extends AppOrderResponse> //表示泛型最高类型是 AppOrderResponse,只能是它或它的子类
IPage<? super AppOrderResponse> //表示泛型最低类型是 AppOrderResponse。只能是它或它的父类

上面都是把无界通配符用在返回值,无界通配符也是可以用在方法参数上的。

使用上界参数

 public void test1(List<? extends AppOrderResponse> list){
        list.add(new AppOrderDispatchedResponse());//添加元素报错,因为我们传入的可能是 List<AppOrderWaitPaidResponse>
        list.add(new AppOrderResponse());//添加元素报错,因为我们传入的可能是 List<AppOrderWaitPaidResponse>
        list.add(null);//这是唯一可以添加的元素 null
        AppOrderResponse appOrderResponse = list.get(0);//接受元素类型为 AppOrderResponse
}

泛型上界参数在方法内只能读取,不能写入。

使用下界参数

public void test2(List<? super AppOrderResponse> list){
    list.add(new AppOrderResponse());//可以添加
    list.add(new AppOrderDispatchedResponse());//可以添加
    Object object = list.get(0);//接受元素的类型为 Object
}

泛型下界参数在方法内只能写入,不能读取。

泛型通配符 "?" 和 T、E、R、K、V 的区别

  • T、E、R、K、V 对于程序运行时没有区别,只是名字不同
  • ? 表示不确定的泛型类型
  • T (type) 表示具体的一个泛型类型
  • K V (key value) 分别代表 Map 中的键值 Key Value
  • E (element) 代表元素,例如 ArrayList 中的元素
  • T 用于定义泛型类和泛型方法
  • "?" 用于方法形参

泛型擦除

所谓的泛型擦除其实很简单,简单来说就是泛型只在编译时起作用,运行时泛型还是被当成 Object 来处理,示例代码

    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("wan");//add 方法形参类型为 String
        String s = list.get(0);//get方法返回值类型为 String
        ArrayList<Integer> list2 = new ArrayList<>();
        System.out.println("list 和 list2 类型相同吗:" + (list.getClass() == list2.getClass()));//true 两个 ArrayList 是同一个类型的
        Method[] methods = list.getClass().getMethods();
        for (Method method : methods) {
            method.setAccessible(true);
            if (method.getName().equals("add")) {
                Class<?>[] parameterTypes = method.getParameterTypes();
                if (parameterTypes.length == 1) {
                    for (Class<?> parameterType : parameterTypes) {
                        System.out.println("add(E e) 形参 E 的类型为:" + parameterType.getName());//泛型的参数 E 运行时是 Object 类型
                    }
                }
            } else if (method.getName().equals("get")) {
                Class<?> returnType = method.getReturnType();
                System.out.println("E get(int index) 的返回值 E 的类型为:" + returnType.getName());
            }
        }
    }

可以看到我们实例化 ArrayList 时虽然传入不同的泛型,但其实它们仍然还是同一个类型。对于 add 方法的形参和 get 方法的返回值,按道理说我们指定的泛型是 String 那么打印出来应该是 String 才对,但是这里运行时得到的却都是 Object,所以这就足以证明了,泛型在编译期起作用,运行时一律被擦除当做 Object 看待,这就是泛型擦除。

参考资料

  1. 一文带你搞懂 Java 泛型 - 知乎 (zhihu.com)
  2. Java 泛型擦除 - hongdada - 博客园 (cnblogs.com)

标签:java,String,ArrayList,list,类型,add,泛型
From: https://www.cnblogs.com/yeahchen/p/17497752.html

相关文章

  • 20230418 java.util.Scanner
    简介publicfinalclassScannerimplementsIterator<String>,Closeable一个简单的文本扫描器,可以使用正则表达式解析原始类型和字符串。Scanner使用分隔符模式将其输入分解为标记,默认情况下匹配空格。然后可以使用各种next方法将生成的标记转换为不同类型的值。Scanner......
  • java 金额显示千分符 java 千分位
    /**@param要格式化的数字;*@return*/*格式化数字为千分位显示;*publicstaticStringfmtMicrometer(Stringtext){DecimalFormatdf=null;if(text.indexOf(".")>0){if(text.length()-text.indexOf(".")-1......
  • java计算两个时间相差天数的方法汇总
    问题描述:输入:两个日期输出:两个日期相差的天数具体代码实现方法1:通过Calendar类的日期比较。注意:这里需要考虑一下:日期是跨年份的,如一个是2012年,一个是2015年的年份是分闰年和平年的,各自的天数不同1234567891011121314151617181920212223242526272829303132333435363738......
  • 宋红康-Java基础复习笔记详细版
    Java基础复习笔记第01章:Java语言概述1.Java基础学习的章节划分第1阶段:Java基本语法Java语言概述、Java的变量与进制、运算符、流程控制语句(条件判断、循环结构)、break\continue、IDEA开发工具的使用、数组第2阶段:面向对象编程(基础、进阶、高级)第3阶段:Java高级应用异常......
  • Excel中PMT计算月供函数的java实现
    Excel中计算月供的公式名叫PMT,有关这个公式的详细描述如下:http://office.microsoft.com/zh-cn/excel-help/HP010342769.aspx下图是Excel中使用这个公式的一个简单说明。Java中实现这个公式可以用下面函数注意,这个函数的所有输入参数都是double类型的。包括支付的月份数,否则计算......
  • 换个姿势,十分钟拿下Java/Kotlin泛型
    0x1、引言解完BUG,又有时间摸鱼学点东西了,最近在复习Kotlin,跟着朱涛的《Kotlin编程第一课》查缺补漏。看到泛型这一章时,想起之前面一家小公司时的面试题:说下你对泛型协变和逆变的理解?读者可以试试在不查资料的情况下能否答得上来?反正我当时是没想起来,尽管写过一篇《Kotlin刨根问底......
  • Java—多线程
    ......
  • Java—反射与注解
    ......
  • Java—集合框架
    什么是集合......
  • Javascript
    什么是Javascript概述javaScript是一门世界上最流行的脚本语言Java,JavaScript10天一个合格的后端人员,必须精通JavaScript历史ECMAScript它可以理解为JavaScript的一个标准最新版本已经到es6版本~但是大部分浏览器还只停留在支持es5代码上!开发环境–线上环境,版本不一致......