首页 > 其他分享 >泛型总结(擦除机制+泛型上界+通配符的上下界)

泛型总结(擦除机制+泛型上界+通配符的上下界)

时间:2024-03-31 21:01:45浏览次数:24  
标签:String Object 通配符 类型 擦除 泛型 new public

文章目录


泛型


一、 什么是泛型

1.能用于多种类型,把类型当做参数

JDK1.5后引入的

1.1 作用

在编译的时候

  • 存储数据的时候,进行自动的类型检查
  • 获取元素的时候,帮助进行类型转换
  • 泛型是编译时期的一种机制,在运行的时候没有泛型的概念

1.2 语法
class MyArray <T>{//当前类是一个泛型类

    //public Object[]  obj = new Object[5];//实现一个类,类中包含一个数组成员,\
    public T[] obj = (T[]) new Object[5];//

    // 使得数组中可以存放任何类型的数据,也可以根据成员方法返回数组中某个
    //下标的值?
    public T getPos(int pos) {
        return this.obj[pos];//回数组中某个
        //下标的值?
    }
    public void setObj(int pos,T val){
        this.obj[pos] = val;
    }
}

public class Test2 {
    public static void main(String[] args) {
        
        MyArray<Integer> myArray = new MyArray<>();//后面的<>可以省略
        //实例化对象的同时,指定当前的泛型类 的 指定参数类型为Integer
        //把类型进行了传递,只能存指定的数据类型
        //MyArray<Integer> myArray = new MyArray<Integer>();
        //指定的参数类型,必须是引用类型
        myArray.setObj(0,3);
        myArray.setObj(1,4);//这时就不能传String类型
        myArray.setObj(2,5);
        Object pos = myArray.getPos(1);
     //  double d = myArray.getPos(2);//不知道是什么类型的,取出的时候很麻烦
        //什么数据都能存储
        //传进去后,都向上转型常量object,获取数据的时候要强转
        //需要指定类型,才能不进行强转
        System.out.println(pos);
        System.out.println("-------------");
        MyArray<String> myArray1 = new MyArray<>();
        myArray1.setObj(1,"hello");
        //获取数据的时候不用强转,里面都是指定的数据类型,直接拿指定的类型接收
        myArray1.setObj(0,"Hello");
        String pos1 = myArray1.getPos(0);
        String pos2 = myArray1.getPos(1);
        //在编译的时候
        //存储数据的时候,进行自动的类型检查
        //获取元素的时候,帮助进行类型转换

    }
}
  • 类名后的 代表占位符,表示当前类是一个泛型类
  • 前面<>指定类型,后面的<>可以省略
  • 把类型进行了传递,只能存指定的数据类型
  • 指定的参数类型,必须是引用类型
  • 不能实例化一个泛型类型的数组
  • 裸类型:不加<>,兼容老版本的机制

E 表示 Element K 表示 Key V 表示 Value N 表示 Number


二、擦除机制

1. 为什么采用擦除机制实现泛型?

为什么Sun公司选择采用擦除机制实现泛型?

向后兼容性
  • 这种设计是为向后兼容性提供源代码和目标代码两方面的支持,希望现有的代码和类文件在新版本的Java中继续使用,而不发生冲突,在不破坏语言的同时,更改语言
移植兼容性
  • 移植兼容性要求API的泛型版本要和老的版本兼容,就是说用户能继续编译(源文件)和运行(编译后的程序)。这种要求在很大程度上限制了Java泛型设计的空间

2. 为什么不能使用“newT()”?

是因为,并不是每个类型都有一个空的构造器

  • Java是静态类型语言,不能简单的调用某一类型的空构造器,因为这个类型自身并不静态地知道有这样的构造器存在。由于擦除机制,所以没有办法来生成这种代码。

3. 创建类型T的数组

3.1 不安全的写法
T[] a = (T[]) new Object[N];

不安全,但可以在多数情况下运行,在编译时会警告
因为Object数组不是具体T类型的数组

如果方法返回T[],并且指定的类型是,将返回值存入String[]类型的变量中,在运行时将得到ClssCastException错误。

		 public T[] copyArr(T[] obj){
      		  return obj;
  	 	 }
        
        public static void main(String[] args) {
        Student<String> student = new Student<>();
        student.setObj(0,"小明");
        student.setObj(1,"小王");
        student.setObj(2,"小红");

        String[] copyArr = student.copyArr(student.obj);
        System.out.println(Arrays.toString(copyArr));
    }
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
	at learn.Test3.main(Test3.java:35)

返回的Object数组里面,可能存放的是任何的数据类型,可能是String,可能是Person,可能是Integer.运行的时候,直接转给String类型的数组,编译器认为是不安全的。

3.2 官方的写法
public Object[] obj =  new Object[5];//正确写法

  return (E) elementData[index];

在Java源码中,使用的是Object类型的数组,在方法中,返回时进行泛型的转换

3. 3 正确的写法

通过反射创建,实现类型的数组

/**
     * 通过反射创建,实现类型的数组
     * @param clazz
     * @param capacity
     */
    public Student(Class<T>clazz, int capacity) {
        this.obj = (T[])Array.newInstance(clazz,capacity);
    }
    Student<String> student = new Student<>(String.class,10);

4. 反编译后,对比方法的参数

在这里插入图片描述

  • 在运行的时候,没有泛型的概念
  • 泛型只是编译时期的一种机制
  • 编译完成后,会将泛类型擦除成了Object类型,叫做擦除机制
  • Java的泛型机制是在编译级别实现的。
  • 编译器生成的字节码在运行期间并不包含泛型的类型信息。

三、泛型的上界

  • 在定义泛型类时,有时需要对传入的类型变量做一定的约束,可以通过类型边界来约束。
  • 泛型是没有下界的

public class MyArray<E extends Number>

E是number的子类 或者 E是Number本身

class Alg<E extends Comparable<E>> {}

代表将来指定的数据类型,一定实现了这个接口

四、泛型方法

  • 静态的泛型方法,要在static后面声明泛型的参数

  •     public static void main(String[] args) {
            List<Integer>list = new ArrayList<>();
            Integer [] arr = {1,2,3,4,5};
           Test2.<Integer>swap(arr,0,2);
            for (int x:arr) {
                System.out.println(x);
            }
        }
        public static<E> void swap(E[]array, int i, int j){
            E t = array[i];
            array[i] = array[j];
            array[j] = t;
        }
    }
    

五、通配符

  • 通配符: “ ?”
class Message<T> {//泛型类
	private T message ;
	public T getMessage() {
	return message;
	}
	public void setMessage(T message) {
	this.message = message;
	}
}
    public static void main(String[] args) {
        Message<String> message = new Message<>() ;
        message.setMessage("Hello world");
        fun(message);//Hello world

        Message<Integer> message2 = new Message<>() ;
        message2.setMessage(888);
       // fun(message2);//fun方法,只能接受<String>类型的对象
        fun(message2);

    }
/*    public static void fun(Message<String> temp){
        System.out.println(temp.getMessage());
    }*/
    public static void fun(Message<?> temp){
        //用通配符来规范传进的参数
        System.out.println(temp.getMessage());
    }

  • 通配符"?"描述的是它可以接收任意类型,但是由于不确定类型,所以无法修改

通配符的上界和下界

? extends 类:设置通配符上限

  • 实例化的只能是参数类型本身或者它的子类
  • 不能进行写入数据(无法确定是哪个子类),只能进行读取数据(向上转型)

? super 类:设置通配符下限

  • 实例化的只能是参数类型本身、或者它的父类

  • 不能进行读取数据(无法确定是哪个父类),只能写入数据(传进去子类对象)

点击移步博客主页,欢迎光临~

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

标签:String,Object,通配符,类型,擦除,泛型,new,public
From: https://blog.csdn.net/m0_64003319/article/details/137209115

相关文章

  • 数据结构_包装类&泛型
    目录一、包装类1.1基本数据类型和对应的包装类1.2装箱和拆箱1.3拓展 二、泛型2.1引出泛型2.2泛型的语法及使用2.3泛型是如何编译的2.3.1擦除机制2.4泛型的上界2.5泛型方法总结一、包装类在Java中,由于基本类型不是继承自Object类,为了在泛型代码中......
  • 蓝旭预习博客2:Java异常类、常用类、容器与泛型
    Java异常类1.什么是异常异常是导致程序中断运行的一种指令流,如果不对异常进行正确处理,则可能导致程序的中断执行,造成不必要的损失。例如下标越界异常(IndexOutOfBoundsException)空指针异常(NullPointerException) 算术异常( ArithmeticException)数字格式化异常(NumberFormatE......
  • 泛型编程(Generic Programming)
    泛型编程(GenericProgramming)虚函数->含有虚函数的类就是抽象类编译(compile)链接(link)转换函数(Conversionfunction)例如将小数转成分数,就是一个转换函数#pragmaonce#ifndef__FRACTION__#define__FRACTION__​classFraction{public://分母不为0,所......
  • java 异常类+常用类+容器、泛型
    目录一、.异常类:1.捕获和处理异常:2.抛出: 3.自定义异常类:二、包装类、String、BigDecimal1.包装类:2.String:3.BigDecimal:三、泛型、列表、set、map:1.泛型:2.列表:3.set:4.map:一、.异常类:异常是程序运行过程中可能出现的错误情况或有别于一般情况的情况。而java......
  • delphi ORM和泛型模板
    delphiORM和泛型模板实现CRUD1)定义数据模型(data-model)数据模型是ORM数据序列/还原所必需的。TTable<T:record>=record//1个表rows:TArray<T>;//表的行end;TTable2<T,T2:record>=record//2个表table1:TTable<T>;......
  • 选择通配符证书还是选择多域名证书?
    随着网络安全意识的提升,SSL/TLS证书已经成为网站安全建设不可或缺的一部分。在选择合适的证书类型时,通配符证书与多域名证书因其能够支持多个域名的特点而受到广泛关注。那通配符证书和多域名证书到底怎么选呢?首先我们先来看一下两者直观的区别通配符SSL证书展示......
  • 泛型的进阶
    1通配符?我们想调用fun函数帮我们打印,但由于不知道Message具体是什么类型,所以我们可以使用:?即通配符当我们将fun函数中改为Message<?>此时就不会报错2通配符的上界:<?extends上界>Demo:<?extendsFruit>意思是传入的实参需要是Fruit或者Fruit的子类当我们用通配......
  • 容器一(泛型、Collection 接口)
    目录泛型Generics自定义泛型容器中使用泛型Collection接口泛型Generics        泛型是JDK1.5以后增加的,它可以帮助我们建立类型安全的集合。在使用了泛型的集合中,遍历时不必进行强制类型转换。JDK提供了支持泛型的编译器,将运行时的类型检查提前到了编......
  • 泛型编程之模板
    1.函数模板重要行:template<typenameT,typenameT1>关键值class和typename含义相同,那么我们以后就使用typename即可。 一般情况下的格式:template<模板参数列表>返回值类型函数名(函数参数) 模板参数列表的理解:函数参数列表在运行时调用者用实参来初始化形参而......
  • 消息sms 邮箱/手机号/push发送的方案 & 定时任务xxlJob灵活度 & 泛型和发送的模板类设
    消息sms邮箱/手机号/push发送的方案&定时任务xxlJob灵活度&泛型和发送的模板类设计1.消息sms邮箱/手机号/push发送的方案1.判断收件人地址是否为空,不为空则发送邮件。为空则不发送。可以通过该方法终止一些消息的发送。2.收件人的地址可以配置在Apollo中,直接删除该key......