首页 > 编程语言 >Java泛型

Java泛型

时间:2024-08-23 20:22:51浏览次数:14  
标签:Java String void 类型 擦除 泛型 public

泛型(Generics)是一种允许在类、接口和方法中使用类型参数的机制,从而在编译时保证类型安全,并提高代码的复用性。泛型提供了一种解决类型不安全问题的方法,并使代码更具可读性和可维护性。

目录

一.泛型类

  • 定义格式:
    一个泛型类的定义包括类型参数。类型参数通常用尖括号(<>)括起来,并放在类名后面。可以使用一个或多个类型参数。
    public class 方法名<泛型类型,.....> {} 
    
  • 注意事项
    • 1.泛型的默认类型是Object,意味着没有加泛型就可以存储任意类型的数据

    • 2.泛型只能编写引用数据类型(包装类在引用数据类型里面),不能用于基本数据类型(int, double, char…)

  • 泛型类型标识符
    常见的有:ETKV
    • 它们不是 Java 语言的专有名词,但它们在泛型编程中有一些约定俗成的含义:
      E: Element(元素),通常用于集合类中,例如 List<E>、Set<E>。
      T: Type(类型),一般用于通用的类型参数,例如 Box<T>。
      K: Key(键),用于表示键的类型,通常出现在映射(Map)相关的泛型定义中,例如 Map<K, V>
      V: Value(值),用于表示值的类型,也出现在映射(Map)相关的泛型定义中,例如 Map<K, V>
      
  • 具体的类型什么时候确认:
    • 创建对象的时候确定到具体的类型,例如:
      //创建Student类
      class Student<E>{    // <E> : 给自己写的类加上了泛型
          private E e;
      
          public E getE() {
              return e;
          }
      
          public void setE(E e) {
              this.e = e;
          }
      }
      
    • 当Student 类实例化时没有明确指定其泛型类型时,默认认为所有的数据类型都是Object类型,这时候可以添加任何类型的数据

      泛型类的类型确定_2

    • 当Student 类实例化时指定泛型类型为String时,只能往里面添加String类型的数据

      泛型类的类型确定_1

二.泛型方法

泛型方法可以分为两种:1.非静态的方法 2.静态的方法
  • 1.非静态的方法
    public class test<E> {
    
        public static void main(String[] args) {
            test<Integer> ex1 = new test();  //泛型参数 E 被指定为 Integer
            ex1.printElement(111);    // E 被推断为 Integer
    
            test<String> ex2 = new test<>();  //泛型参数 E 被指定为 String
            ex2.printElement("张三");  // E 被推断为 String
        }
    
        // 泛型方法定义
        public void printElement(E e) {
            System.out.println(e);
        }
    }
    
    
    • 当text类被实例化时,泛型参数 E 被指定为 Integer时,只能使用Integer类型的数据

      泛型方法的类型确定_1

    • 当text类被实例化时,泛型参数 E 被指定为 String时,只能使用String类型的数据

      泛型方法的类型确定_2

结论:非静态的方法内部的泛型,会根据类的泛型去匹配

  • 2.静态的方法
    public class GenericsDemo03{
        public static void main(String[] args) {
            String[] arr1 = {"张三","李四","王五"};
            Integer[] arr2 = {11,22,33};
            Double[] arr3 = {11.1,22.2,33.3};
    
            printArray(arr1);
            printArray(arr2);
            printArray(arr3);
    
        }
    
        //这个类型 T 在调用方法时由传递的数组类型确定
        public static<T> void printArray(T[] arr){  
            System.out.print("[");
            for (int i = 0; i < arr.length-1; i++) {
                System.out.print(arr[i] + ",");
            }
            System.out.println(arr[arr.length-1] + "]");
        }
    
    }
    
    • 当往泛型方法中传入参数时,会根据传入的参数类确定确定具体的类型。下面arr1,arr2,arr3就分别对应了String类型,Integer类型,Double类型。

      泛型方法的类型确定_3

结论:静态方法中如果加入了泛型,必须申明出自己独立的泛型。在调用方法,传入实际参数的时候,确定到具体的类型

三.泛型接口

泛型接口有两种方式确定自己的具体类型:
1.实现类,实现接口的时候确定到具体的类型
2.实现类实现接口,没有指定具体类型,就让接口的泛型,跟着类的泛型去匹配
1.实现类,实现接口的时候确定到具体的类型
  • 具体代码如下:
    public class test {
    
        public static void main(String[] args) {
    
        }
    }
    
    //自定义接口
    interface inter<E> {
        //接口中的抽象方法,方法的形参类型没想好,可以加一个泛型,然后在接口上也加泛型
        void show(E e);   
    }
    
    
    //1:实现类,实现接口的时候确定到具体的类型
    class InterAImpl implements inter<String>{
    
        @Override
        public void show(String s) {
    
        }
    }
    
2.实现类实现接口,没有指定具体类型,就让接口的泛型,跟着类的泛型去匹配
  • 具体代码如下:
    public class test {
        public static void main(String[] args) {
    
        }
    }
    
    interface inter<E> {
    	//接口中的抽象方法,方法的形参类型没想好,可以加一个泛型,然后在接口上也加泛型
        void show(E e);   
    }
    
    //2.实现类实现接口,没有指定具体类型,就让接口的泛型,跟着类的泛型去匹配
    class InterBImpl<E> implements inter<E>{  //实现类实现接口的时候,还没想好类型,还可以继续申明泛型
        @Override
        public void show(E e) {
    
        }
    }
    
    • 当text类被实例化时,泛型参数 E 被指定为 String时,只能使用String类型的数据

      泛型接口的类型确定_1

    • 当text类被实例化时,泛型参数 E 被指定为 Integer时,只能使用Integer类型的数据

      泛型接口的类型确定_2

四.通配符(Wildcard)

  • 无界通配符(?):表示任何类型
    • 例如:
       // 可以是任何类型的 List
      List<?> list = new ArrayList<String>();
      
  • 上界通配符(? extends T):表示匹配 T 类型或其子类型
    • 例如:
      // 可以是 List<Number> 或 List<Integer>
      List<? extends Number> list = new ArrayList<Integer>(); 
      
  • 下界通配符(? super T):表示匹配 T 类型或其父类型
    • 例如:
      // 可以是 List<Number> 或 List<Object>
      List<? super Integer> list = new ArrayList<Number>(); 
      

五.泛型的限制

  • 不能创建泛型数组:由于泛型类型在运行时被擦除,不能直接创建泛型数组。例如,new T[10] 是不允许的。
  • 不能使用基本数据类型:泛型不能用于基本数据类型,如 intchar 等。需要使用对应的包装类,如 IntegerCharacter
  • 不能实例化泛型类型:不能直接实例化泛型类型,例如 new T() 是不允许的。可以通过传递类类型或使用反射来解决这个问题。
  • 类型擦除:泛型类型在编译时会被擦除为其边界类型(通常是 Object),这意味着在运行时泛型类型的信息丢失。
  • 静态上下文限制:泛型类型不能用于静态字段或静态方法中,因为这些静态成员不依赖于具体的泛型类型。

六.类型擦除

基本概念:
Java中的泛型是通过类型擦除实现的。类型擦除是指在编译时,泛型类型信息会被擦除,转化为其边界类型或 Object。这意味着在运行时,泛型信息不可用。这种设计使得泛型与Java的旧代码兼容,但也带来了一些限制,如无法在运行时获取泛型参数的实际类型。

如何工作

1.类型擦除:
  • 泛型类型 T 会被擦除为其上限类型。例如,List<String>List<Integer> 都会被擦除为 List
  • 如果泛型有一个上限(如 T extends Number),T 会被擦除为这个上限(即 Number)。
2.泛型方法和类型参数:
  • 泛型方法的类型参数在方法内部使用,但在编译后会被擦除。例如,public <T> void method(T param) 经过编译后,T 会被替换为 Object(如果没有明确的上限)。
3.实例化泛型类型:
  • 由于类型擦除,不能直接使用泛型类型进行实例化,如 new T()。泛型类型的创建需要通过反射来完成。
4.类型检查:
  • 泛型擦除确保了在运行时泛型类型的类型安全性,通过编译器的检查来避免类型错误。

示例代码:

  • 编译前:
    public class Box<T> {
        private T value;
    
        public void setValue(T value) {
            this.value = value;
        }
    
        public T getValue() {
            return value;
        }
    }
    
  • 编译后:
    public class Box {
        private Object value;  //泛型类型参数 T 被擦除为 Object
    
        public Box() {
        }
    
        public void setValue(Object value) {  //泛型类型参数 T 被擦除为 Object
            this.value = value;
        }
    
        public Object getValue() {     //泛型类型参数 T 被擦除为 Object
            return value;
        }
    }
    

标签:Java,String,void,类型,擦除,泛型,public
From: https://blog.csdn.net/m0_72057247/article/details/141474126

相关文章

  • [Javascript] Covert async code to sync code with throwing Promise
    constfetch=()=>newPromise((res)=>{setTimeout(()=>res({user:'zhen'}),1150)})globalThis.fetch=fetchasyncfunctiongetUser(){returnawaitfetch()}asyncfunctionm1(){//dosomethingreturnawaitge......
  • Java中Object类的学习
    Object类目录Object类object的介绍object类提供了十一个方法object的介绍Object类是Javajava.lang包下的核心类,Object类是所有类的父类,何一个类时候如果没有明确的继承一个父类的话,那么它就是Object的子类;以下两种类的定义的最终效果是完全相同的:classPerson{}classPe......
  • JavaSE基础知识分享(十四)
    写在前面今天继续讲Java中的类加载器和lambda表达式的知识!类加载器和反射类的加载当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载、连接、初始化三步来实现对这个类进行初始化。加载将.class文件读入内存,并为之创建一个Class对象。任何类被使用时系......
  • 【JavaEE初阶】滑动窗口和流量控制以及拥塞控制
    目录......
  • Java毕业设计作品(119):基于thymeleaf前后端分离 驾校会员报名和管理系统设计与实现
      博主介绍:黄菊华老师《Vue.js入门与商城开发实战》《微信小程序商城开发》图书作者,CSDN博客专家,在线教育专家,CSDN钻石讲师;专注大学生毕业设计教育和辅导。所有项目都配有从入门到精通的基础知识视频课程,学习后应对毕业设计答辩。项目配有对应开发文档、开题报告、任务书......
  • JavaSE基础(12)——文件、递归、IO流
    1、IO流Input:输入,写数据,数据从磁盘加载到内存(程序)中。Output:输出,读数据,数据从内存(程序)存储到磁盘中。流:不管是读还是写,都要对数据进行传输,传输的方式就是流2、File类数据的读写离不开文件,File类是可以对文件和目录(文件夹)级别,而不是内容本身进行增删改查的类。File类的API......
  • Java常用API第二篇
    正则表达式: 正则表达式(简称regex)是用来描述字符串模式的工具,常用于字符串的查找、匹配、替换等操作。它在文本处理、数据验证、以及编程中非常常见。以下是正则表达式的基本知识点:1.正则表达式的基础符号.(点):匹配除换行符\n以外的任何单个字符。例如,正则表达式......
  • 文心快码Baidu Comate 帮你解大厂面试题:在8g内存的机器,能否启动一个7G堆大小的java进
    ......
  • Java IO
    JavaI/OIO4个抽象类:InputStream、OutputStream、Reader、WriterInputStream、OutputStream操作字节byte[]读取,写入。Reader、Writer操作字符char[]读取写入。一字节流1.InputStreamintread():读取数据intread(byteb[],intoff,intlen):从第off位置开始读,读......
  • Java异常-介绍
    异常的基本概念异常(Exception)是程序在运行时发生的、不正常的或意外的状况,它打断了程序的正常执行流程。在编程中,异常用于处理错误情况,使得程序能够以一种优雅的方式响应错误,而不是简单地崩溃或终止执行。异常的定义在Java等编程语言中,异常被定义为一种特殊的对象,它是Throwa......