当前位置: 首页 > news >正文

Java泛型详解

文章目录

  • 01 什么是泛型
  • 02 泛型类
    • 泛型类的使用
    • 泛型的派生子类
  • 03 泛型接口
  • 04 泛型方法
  • 05 类型通配符
  • 06 类型擦除
  • 07 泛型和数组
  • 08 泛型和反射


01 什么是泛型

泛型产生的背景:Java推出泛型以前,是构建一个元素类型为 Object 的集合,该集合能够存储任意的对象,而在使用该集合的过程中,需要明确知道存储每个元素的数据类型,否则很容易引发 ClassCastException 异常(类型转换异常)。

  • 泛型的概念

       Java泛型(generics)是 JDK5 中引入的一个新特性,泛型提供了编译时类型安全检测机制,该机制允许在编译时检测到非法的类型数据结构。

       泛型的本质就是参数化类型,也就是所操作的数据类型被指定为一个参数。

  • 泛型相当于提供了一个安全限制,不符合类型转换的会报错
import java.util.ArrayList;

public class Test01 {
    public static void main(String[] args) {
        /*   
        ArrayList list = new ArrayList();//创建一个集合
        list.add("java");//默认数据类型为Object
        list.add(100);
        list.add(true);

        for (int i = 0 ;i < list.size();i++){
            Object o = list.get(i);
            String str = (String)o;
            System.out.println(str); //会报错 ClassCastException
        }  */

        //使用泛型(作为一个安全检测限制)
        ArrayList<String> strList = new ArrayList<>();
        strList.add("a");
        strList.add("b");
        strList.add("c");

        for (int i = 0; i < strList.size(); i++) {
            String s = strList.get(i);
            System.out.print(s);//abc
        }
        //泛型的好处:编译期间检查类型,减少了数据类型转换
        // <> 数据类型作为参数 
        ArrayList<Integer> intList = new ArrayList<>();
        intList.add(100);//自动转换为int类型
        //如果输入浮点数、字符串等其他类型就会报错
    }
}

02 泛型类

泛型类的使用

泛型类的定义语法:

class 类名称 < 泛型标识,泛型标识 ,... > {
	private 泛型标识 变量名;
	....
}
  • 泛型标识可以理解为类型的形参
  • 常用的泛型标识: T 、 E 、 K 、 V
    • E ----Element(在集合中使用,因为集合中存放的是元素)

    • T ----Type(Java类)

    • K ----Key(键)

    • N ----Number(数值类型)

    • V ----Value(值)

    • ?----表示不确定的java类型

      • 这些标记并不是限定只有对应的类型才能使用,即使你统一使用A-Z英文字母的其中一个,编译器也不会报错。之所以又不同的标记符,这是一种约定
/*
泛型类的定义:
    <T> 泛型标识--类型形参
     T 创建对象的时候里指定具体的数据类型
 */
public class Test02 <T>{
    // T 是由外部使用类的时候来指定
    private T key;
    
    public Test02 (T key) {
        this.key = key;
    }
   
    public T getKey(){
        return key;
    }
   
    public void setKey(T key){
        this.key = key;
    }

    @Override
    public String toString() {
        return "Test02{" +
                "key=" + key +
                '}';
    }
}

泛型类的使用:

  • 使用语法:
类名<具体的数据类型> 对象名 = new 类名 <具体的数据类型>();
  • Java1.7以后,后面的<>中的具体的数据类型可以省略不写
类名<具体的数据类型> 对象名 = new 类名<>();
/*
泛型类的定义:
    <T> 泛型标识--类型形参
     T 创建对象的时候里指定具体的数据类型
 */
public class Test02 <T>{
    // T 是由外部使用类的时候来指定

    public static void main(String[] args) {
        //泛型类在创建对象的时候,来指定操作的具体数据类型
        Test02<String> strTest = new Test02<>("abc");//只能传指定的类型数据,不然会报错
        String key1 = strTest.getKey();
        System.out.println(key1);//abc
        //定义一个泛型类,可以操作不同的数据类型,减少了转换,提高了代码的复用率
        Test02<Integer> intTest = new Test02<>(100);//只能传指定的类型数据,不然会报错
        int key2 = intTest.getKey();
        System.out.println(key2);//100
        //泛型类不支持基本数据类型(如<int>错误),只支持类类型(来继承Object类接收类型转换)

        //泛型类在创建对象的时候,没有指定类型,将按照Object类型来操作
        Test02 test = new Test02("ABC");
        Object key3 = test.getKey();
        System.out.println(key3);//ABC

        System.out.println(strTest.getClass());//class com.practice.fanxing.Test02
        System.out.println(intTest.getClass());//class com.practice.fanxing.Test02
        System.out.println(intTest.getClass() == strTest.getClass());//true 说明就是同一个类
        //同一泛型类,根据不同类型的数据创建的对象,本质上是同一类型
    }
    private T key;

    public Test02 (T key) {
        this.key = key;
    }

    public T getKey(){
        return key;
    }

    public void setKey(T key){
        this.key = key;
    }

    @Override
    public String toString() {
        return "Test02{" +
                "key=" + key +
                '}';
    }
}

泛型类注意事项:

  1. 泛型类,如果没有指定具体的数据类型,此时操作类型是Object;
  2. 泛型的类型参数只能是类类型,不能是基本数据类型;
  3. 泛型类型在逻辑上可以看成是多个不同的类型,但实际上都是相同类型;

泛型的派生子类

  • 子类也是泛型类,子类和父类的泛型类型要一致;
class ChildGeneric<T> extends Generic<T>;

在这里插入图片描述

  • 子类不是泛型类,父类要明确泛型的数据类型;
class ChildGeneric extends Generic<String>;

在这里插入图片描述


03 泛型接口

  • 泛型接口的定义语法:
interface 接口名称 <泛型标识,泛型标识,...> {
	泛型标识 方法名();
	   ...
}
  • 泛型接口的使用:
    • 实现类不是泛型类,接口要明确数据类型;
    • 实现类也是泛型类,实现类和接口的泛型类型要一致;

实现类不是泛型类:

在这里插入图片描述
实现类是泛型类:
在这里插入图片描述


04 泛型方法

  • 泛型类,是在实例化类的时候指明泛型的具体类型;
  • 泛型方法,是在调用方法的时候指明泛型的具体类型;

语法:

修饰符 <T,E,...> 返回值类型 方法名(形参列表){
	方法体...
}
  • public和返回值中间的 < T > 可以理解为声明此方法为泛型方法;
  • 只有声明了 < T > 的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法;
  • < T > 表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T;
  • 与泛型类的定义一样,此处T可以随便写为任意标识,常见的如 T、E、K、V 等形式的参数常用于表示泛型;

在这里插入图片描述

public class Method01<E> {

    //这不是泛型方法
    public E method00(){return null;}
    /* 不能声明为静态Static的 */
    
    //这才是泛型方法 可以声明为静态的Static
    public <E> E method01 ( E input ){
        return input;
    }

    public static void main(String[] args) {
        Method01<String> str = new Method01<>();
        Method01<Integer> integer = new Method01<>();
        String s = str.method01("Hello");
        Integer i = integer.method01(100);
        System.out.println(s);//Hello
        System.out.println(i);//100
    }
}

泛型方法和可变参数:

public <E> void print (E... e){ //就是加上三个点
	for (E eee : e){
	System.out.println(e);
	}
}

在这里插入图片描述

  • 泛型方法能使方法独立于类而产生变化;
  • 如果 static 方法要使用泛型能力,就必须要定义为泛型方法;

05 类型通配符

什么是类型通配符:

  • 类型通配符一般是使用 < ? > 代替具体的类型实参;
  • 所以,类型通配符是类型实参,而不是类型形参;

为什么要用通配符:

  • 例如一个方法定义了< Number >类型,而调用时声明的< Integer >类型(虽然Integer继承Number,所以也没法重写)会报错,这时想支持多种类型,可以用通配符 < ? >;
  • < ? > 定义的 用 Object 类型来接收
public static void show(Box<?> box){
	 Object value = box.getValue();
}

类型通配符的上限:

  • 语法
/接口 <? extends 实参类型>

要求该泛型的类型,只能是实参类型,或实参类型的子类类型;
(也就是泛型的类型最高的上限)

public static void show(Box<? extends Number> box){
	 Number value = box.getValue();//类型最大为Number
}

类型通配符的下限:

  • 语法
/接口 <? super 实参类型>

要求该泛型的类型,只能是实参类型,或实参类型的父类类型;


06 类型擦除

       泛型是Java1.5版本才引进的概念,在这之前是没有泛型的,但是,泛型代码能够很好地和之前版本的代码兼容,是因为,泛型信息只存在于代码编译阶段,在进入JVM之前,与泛型相关的信息会被擦除,称为----类型擦除

import java.util.ArrayList;

public class Test04 {
    public static void main(String[] args) {
        ArrayList<Integer> intList = new ArrayList<>();
        ArrayList<String> strList = new ArrayList<>();
        // 输出类型是一样的,说明经过了类型擦除
        System.out.println(intList.getClass().getSimpleName());//ArrayList
        System.out.println(strList.getClass().getSimpleName());//ArrayList
        System.out.println(intList.getClass() == strList.getClass());//true
    }
}

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  • 当重写的方法返回值是父类方法返回值的子类时会生成一个桥接方法来实现父类的重写

在这里插入图片描述

  • 实现类对接口的方法进行了重写实现,桥接方法又对重写的方法再重写,保证输入和返回值和接口的一致

07 泛型和数组

泛型数组的创建:

  • 可以声明带泛型的数组引用,但是不能直接创建带泛型的数组对象;
import java.util.ArrayList;

//泛型和数组
public class Test07 {
    public static void main(String[] args) {
      /*  ArrayList<String>[] listArray = new ArrayList<String>[5];
                 可以声明带泛型的数组        但不能直接创建带泛型的数组对象
       */
        ArrayList[] list1 = new ArrayList[5];
        ArrayList<String>[] listArray1 = list1;
        //先创建一个普通数组,再赋给泛型数组
        //   但这样是有弊端的,不安全,
        // 因为定义了是String类型的,如果给原生list1赋值传递Integer类型会导致类型转换异常
        // 把list1隐藏起来,就可以解决,因为编译器会进行泛型检查
        ArrayList<String>[] listArray2 = new ArrayList[5];
        
        ArrayList<String> strList = new ArrayList<>();
        strList.add("abc");
        listArray2[0] = strList;
        String s = listArray2[0].get(0);
        System.out.println(s);
    }
}

  • 可以通过 java.lang.reflect.Array 的 newInstance (Class< T >,int) 创建 T [ ] 数组;

在这里插入图片描述
先用一个泛型类来封装一个泛型数组

import java.lang.reflect.Array;

//泛型会经历一个擦除,但数组会从始至终保持数据类型;
//所以JDK不允许直接创建带泛型的数组;
//可以通过java.lang.reflect.Array的newInstance(Class<T>,int)创建T[]数组
public class Test08<T> {
    private T[] array;
    //通过构造方法创建泛型数组  传递一个泛型类和数组长度
    public Test08(Class<T> clz,int length){
        array = (T[])Array.newInstance(clz,length);//需要强转
    }
    //封装起来
    public void put(int index,T item){
        array[index] = item;  //填充数组
    }

    public T get(int index){
        return array[index]; //获取数组元素
    }

    public T[] getArray(){
        return array;  //获取整个数组
    }
}

再通过调用来操作数组

import java.util.Arrays;

public class Test09 {
    public static void main(String[] args) {
        Test08<String> arr = new Test08<>(String.class,3);
        arr.put(0,"apple");
        arr.put(1,"banana");
        arr.put(2,"orange");
        //先获取数组再通过Arrays.toString方法全部打印出来 (就不用遍历)
        System.out.println(Arrays.toString(arr.getArray()));
           // [apple, banana, orange]
        String s1 = arr.get(2);
        System.out.println(s1);//orange
    }
}

08 泛型和反射

反射常用的泛型类:

  • Class < T >
  • Constructor < T >

在这里插入图片描述

相关文章:

  • opencv连通域标记 connectedComponentsWithStats()函数
  • 【C#在资源管理器中显示自定义文件格式的缩略图】
  • 【NLP】第2章 开始使用 Transformer 模型的架构
  • 电容的分类
  • MYBatis-Plus常用注解@TableName、@TableId、@TableField、@TableLogic
  • 沉睡者IT - 贡献者和律师的Web3指南:充分去中心化
  • unityEditor扩展开发onGUI添加右键菜单
  • 认识 fcntl 接口函数
  • a16z:呼吁SEC改革加密资产托管规则的建议
  • 通过虚拟代理服务器解决Axios跨域问题[Vue.js项目实践: 新冠自检系统]
  • 【JavaScript-动画原理】如何使用js进行动画效果的实现
  • Servlet对象的生命周期
  • python入门基础-数据类型有序序列和无序序列;
  • MySQL 高级SQL语句 (二)
  • 小米面试——计算机视觉算法实习生
  • [js高手之路]搞清楚面向对象,必须要理解对象在创建过程中的内存表示
  • CentOS7 安装JDK
  • Debian下无root权限使用Python访问Oracle
  • LeetCode541. Reverse String II -- 按步长反转字符串
  • PAT A1120
  • React中的“虫洞”——Context
  • vue 配置sass、scss全局变量
  • 那些被忽略的 JavaScript 数组方法细节
  • 如何设计一个比特币钱包服务
  • 使用agvtool更改app version/build
  • 微信小程序设置上一页数据
  • 无服务器化是企业 IT 架构的未来吗?
  • 掌握面试——弹出框的实现(一道题中包含布局/js设计模式)
  • ​LeetCode解法汇总2583. 二叉树中的第 K 大层和
  • ​插件化DPI在商用WIFI中的价值
  • (003)SlickEdit Unity的补全
  • (23)Linux的软硬连接
  • (js)循环条件满足时终止循环
  • (Redis使用系列) Springboot 使用redis实现接口幂等性拦截 十一
  • (保姆级教程)Mysql中索引、触发器、存储过程、存储函数的概念、作用,以及如何使用索引、存储过程,代码操作演示
  • (附源码)spring boot网络空间安全实验教学示范中心网站 毕业设计 111454
  • (附源码)springboot太原学院贫困生申请管理系统 毕业设计 101517
  • (轉貼) 寄發紅帖基本原則(教育部禮儀司頒布) (雜項)
  • *1 计算机基础和操作系统基础及几大协议
  • *上位机的定义
  • .NET 使用 XPath 来读写 XML 文件
  • .NetCore部署微服务(二)
  • .net经典笔试题
  • .net开发时的诡异问题,button的onclick事件无效
  • .one4-V-XXXXXXXX勒索病毒数据怎么处理|数据解密恢复
  • @kafkalistener消费不到消息_消息队列对战之RabbitMq 大战 kafka
  • @Validated和@Valid校验参数区别
  • @拔赤:Web前端开发十日谈
  • [ vulhub漏洞复现篇 ] ECShop 2.x / 3.x SQL注入/远程执行代码漏洞 xianzhi-2017-02-82239600
  • [ 第一章] JavaScript 简史
  • [Android学习笔记]ScrollView的使用
  • [ASP.NET MVC]如何定制Numeric属性/字段验证消息
  • [AX]AX2012 AIF(四):文档服务应用实例
  • [BZOJ1089][SCOI2003]严格n元树(递推+高精度)
  • [C#]winform制作圆形进度条好用的圆环圆形进度条控件和使用方法