java泛型初阶和包装类

news2024/12/28 5:59:34

文章目录

    • 1 包装类
    • 6 泛型如何编译的
    • 6.1 擦除机制
      • 6.2 为什么不能实例化泛型类型数组
    • 7 泛型的上界
      • 7.1 语法
      • 7.2 示例
      • 7.3 复杂示例
    • 8 泛型方法
      • 8.1 定义语法
      • 8.2 示例
      • 8.3 使用示例-可以类型推导
      • 8.4 使用示例-不使用类型推导

1 包装类

在Java中,由于基本类型不是继承自Object,为了在泛型代码中可以支持基本类型,Java给每个基本类型都对应了
一个包装类型。

6 泛型如何编译的

6.1 擦除机制

那么,泛型到底是怎么编译的?这个问题,也是曾经的一个面试问题。泛型本质是一个非常难的语法,要理解好他
还是需要一定的时间打磨。

通过命令:javap -c 查看字节码文件,所有的T都是Object。
在这里插入图片描述

在编译的过程当中,将所有的T替换为Object这种机制,我们称为:擦除机制。

Java的泛型机制是在编译级别实现的。编译器生成的字节码在运行期间并不包含泛型的类型信息。

有关泛型擦除机制的文章截介绍:https://zhuanlan.zhihu.com/p/51452375

提出问题:

1、那为什么,T[] ts = new T[5]; 是不对的,编译的时候,替换为Object,不是相当于:Object[] ts = new
Object[5]吗?

2、类型擦除,一定是把T变成Object吗?

6.2 为什么不能实例化泛型类型数组

小结: 基类数组不能直接强制类型转换赋值给子类数组,因为基类数组可能存在其他类型的子类

讲解步骤:

  • 抛出问题代码
  • 分析代码错误原因
  • 具体解释错误原因
  • 解决问题
  • 开发中正确写法

代码1:

class MyArray<T> {
	public T[] array = (T[])new Object[10];//泛型类型数组
	
	public T getPos(int pos) {
		return this.array[pos];
	}
	
	public void setVal(int pos,T val) {
	this.array[pos] = val;
		}
	
	public T[] getArray() {
		return array;
	}
}
public static void main(String[] args) {
		MyArray<Integer> myArray1 = new MyArray<>();
			Integer[] strings = myArray1.getArray();//erro
}	
/*
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
at TestDemo.main(TestDemo.java:31)
*/

原因:擦除替换后, 将Object[]分配给Integer[]引用,程序报错。

// 替换前
public T[] getArray() {
	return array;
}
// 替换后
public Object[] getArray() {
	return array;
}

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

如下两点详细解释

1.如下代码,在java中,new必须指定确定类型

class MyArray<T> {
    //public T[] array = (T[])new Object[10];
    public T[] array = new T[2];
    public T getPos(int pos) {
        return this.array[pos];
    }
    public void setVal(int pos,T val) {
        this.array[pos] = val;
    }
    public T[] getArray() {
        return array;
    }
}

2.在Java中,不能将Object类型的数组分配给子类的数组,因为这会违反Java的类型安全性规则。如果可以这样做,那么子类数组可能包含其它类型的元素,从而导致类型转换异常或者其他运行时错误。要将一个Object类型的数组分配给子类的数组,需要使用强制类型转换并确保所有元素都是正确的子类类型。
例如:

ojbecj对象是所有类的基类,下面是基类与子类的例子,假设有一个父类Animal和两个子类Cat和Dog,如果想要将一个Object类型的Animal数组分配给一个Cat类型的数组,需要进行以下强制类型转换:

    public static void main(String[] args) {
 // erro1:
      Animal[] animalArray = new Animal[2];
      Cat[] catArray = (Cat[]) animalArray;//erro
    }
     // 报错:
		//Exception in thread "main" java.lang.ClassCastException: Dog cannot be cast to Cat
		//at genericArray.main(genericArray.java:68)

但是这样做会导致运行时错误,因为animalArray实际上并不是一个Cat数组,其中可能包含其他类型的Animal对象。

正确的做法是,使用for循环逐一将Object类型的元素转换为Cat类型,并添加到Cat类型的新数组中,例如:

    public static void main(String[] args) {
//  success:
        Animal[] animalArray = new Animal[2];
        Cat[] catArray = new Cat[2];
        for (int i = 0; i < animalArray.length; i++) {
            catArray[i] = (Cat) animalArray[i]; // 进行强制类型转换
        }
    }

这样可以确保每个元素都被正确地转换为Cat类型。但是需要注意,如果animalArray中包含了除Animal以外的其它类型的对象,则会在转换时抛出ClassCastException异常。

    public static void main(String[] args) {
//  erro2:        
       Animal[] animalArray = new Animal[2];
       animalArray[1] = new Dog(); // Animal[]数组包含了其他Animal子类
    	Cat[] catArray = new Cat[2];
       for (int i = 0; i < animalArray.length; i++) {
           catArray[i] = (Cat) animalArray[i]; // 进行强制类型转换
           //erro i==1时,Dog错误类型转换
      }
 // 报错:
//Exception in thread "main" java.lang.ClassCastException: Dog cannot be cast to Cat
	//at genericArray.main(genericArray.java:68)

解决办法: 【了解即可,开发中不会用到】

  • 有点神奇!通过反射创建,指定类型的数组
class MyArray1<T> {
    public T[] array;

    public MyArray1() {
    }
    public MyArray1(Class<T> clazz, int capacity) {
        array = (T[]) Array.newInstance(clazz, capacity);
    }
    public T getPos(int pos) {
        return this.array[pos];
    }
    public void setVal(int pos,T val) {
        this.array[pos] = val;
    }
    public T[] getArray() {
        return array;
    }
}

实际开发是这样的:

class MyArray3<T> {
    //public T[] array = (T[])new Object[10];
    public Object[] array = new Object[2];
    public T getPos(int pos) {
        return (T)array[pos];
    }
    public void setVal(int pos,T val) {
        this.array[pos] = val;
    }
    public Object[] getArray() {
        return array;
    }
}
public class genericArray {
    public static void main(String[] args) {
        MyArray<Integer> myArray = new MyArray<>();
        myArray.setVal(0,1);
        myArray.setVal(1,0);
        Object a = myArray.getPos(0);
        System.out.println(a);
        // 不用转换类型也可以的,因为返回的就是object
        Object b = (Object)myArray.getPos(1);
        System.out.println(b);
        //Integer[] int_array = myArray.getArray(); erro 基类数组不能给子类
        // 获取数组后,访问数组元素需要强制转换.
        Object[] int_array = myArray.getArray();
        System.out.println((Integer)int_array[0]);
    }
}

7 泛型的上界

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

7.1 语法

class 泛型类名<泛型新参 extend 类型边界>{

}

具体意思通过例子说明。

7.2 示例

public class MyArray<E extends Number> {
........

}

接受 Number 的子类型作为 E 的类型实参

MyArray<Integer> l1; // 正常,因为 Integer 是 Number 的子类型
MyArray<String> l2; // 编译错误,因为 String 不是 Number 的子类型
error: type argument String is not within bounds of type-variable E
MyArrayList<String> l2;
^
where E is a type-variable:
E extends Number declared in class MyArrayList

了解: 没有指定类型边界 E,可以视为 E extends Object

7.3 复杂示例


// #2 语法规定只允许有使用Comparable接口的类型
class Alg<T extends Comparable<T>>{

    public T findMax(T[] array){
        T max = array[0];
        for(int i=1;i<array.length;i++){
            // if( max < array[i]) #1 erro : 引用类型无法比较
            // 只能实现接口compareTO()
            if(max.compareTo(array[i])<0)
            {
                max = array[i];
            }
        }
        return max;
    }
}
public class Main {
    public static void main(String[] args) {
        Alg<Integer> alg = new Alg();
        Integer[] array = {1,2,3,4,5};
        // 根据array自动类型推导
        System.out.println(alg.findMax(array));
        // 新语法 <类型> 指定类型
        System.out.println(alg.<Integer>findMax(array));
        // Alg<int> alg2 = new Alg(); // erro
        //#3
        // Alg<int> alg2 = new Alg(); // erro
    }
}

8 泛型方法

8.1 定义语法

方法限定符 <类型形参列表> 返回值类型 方法名称(形参列表) { ... }

8.2 示例

静态的泛型方法 需要在static后用<>声明泛型类型参数

class Alg1{

    public static<T extends Comparable<T>> T findMax(T[] array){
        T max = array[0];
        for(int i=1;i<array.length;i++){
            // if( max < array[i]) #1 erro : 引用类型无法比较
            // 只能实现接口compareTO()
            if(max.compareTo(array[i])<0)
            {
                max = array[i];
            }
        }
        return max;
    }
}

 public class Main {
     public static void main(String[] args) {
         Integer[] array = {1,2,3,4,5};

         // 根据array自动类型推导
         System.out.println(Alg1.findMax(array));
         // 新语法 <类型> 指定类型
         System.out.println(Alg1.<Integer>findMax(array));
     }
  }

8.3 使用示例-可以类型推导

 public class Main {
     public static void main(String[] args) {
         Integer[] array = {1,2,3,4,5};

         // 根据array自动类型推导
         System.out.println(Alg1.findMax(array));

     }
  }

8.4 使用示例-不使用类型推导

 public class Main {
     public static void main(String[] args) {
         Integer[] array = {1,2,3,4,5};


         // 新语法 <类型> 指定类型
         System.out.println(Alg1.<Integer>findMax(array));
     }
  }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/565676.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Dubbo源码篇07---SPI神秘的面纱---原理篇---下

Dubbo源码篇07---SPI神秘的面纱---原理篇---下 引言根据name获取扩展实例对象获取默认扩展实例对象按条件批量获取扩展实例对象实例演示 引言 上篇文章&#xff1a; Dubbo源码篇06—SPI神秘的面纱—原理篇—上 我们追踪了getAdaptiveExtension获取自适应扩展点的整个流程&…

ChatGPT“保姆级教程”——手把手教你1分钟快速制作思维导图(Markmap/Xmind+Markdown)

目录 前言使用ChatGPT生成markdown格式主题Markmap Markdown使用Markmap生成思维导图 Xmind Markdown使用Xmind生成思维导图 建议其它资料下载 前言 思维导图是一种强大的工具&#xff0c;它可以帮助我们整理和展现复杂的思维结构&#xff0c;提升我们的思考能力和组织能力。…

chatgpt赋能python:Pythonsort逆序:使你的排序更有效

Python sort 逆序&#xff1a;使你的排序更有效 排序是计算机科学中最基本的操作之一&#xff0c;因为排序可以使计算机按某种有序的方式访问数据。Python sort 函数是一个非常有用的函数&#xff0c;它可以对列表或元组进行排序&#xff0c;但是&#xff0c;有时候我们需要对…

Win10 / 11新电脑最简单跳过联网激活和使用本地账户登录方法

跳过联网激活&#xff1a; OOBE界面直接按CtrlShiftF3进入审核模式。这样就可以直接进入系统进行一些硬件测试等&#xff0c;而不用联网激活导致新机无法退货。 需要注意的是&#xff0c;在审核模式下进行的一些操作都会保留&#xff0c;并不会在退出后自动还原&#xff01;安…

感谢飞书放过幕布!GPT-4平替Poe;100个GPT-4实战案例;AI绘画新手指南之SD篇;new Bing靠谱教程;AI生成视频摘要神器 | ShowMeAI日报

&#x1f440;日报合辑 | &#x1f3a1;生产力工具与行业应用大全 | &#x1f9e1; 点赞关注评论拜托啦&#xff01; &#x1f916; 『感谢飞书放过幕布』flomo 从字节收购幕布&#xff0c;时代变了 3月15日&#xff0c;flomo 浮墨笔记团队已与飞书初步达成全资收购幕布的合作意…

期末复习总结!!【MySQL】五种约束类型, 主键和外键的使用方式(重点)

文章目录 前言一、约束类型二、NOT NULL三、UNIQUE四、DEFAULT五、PRIMARY KEY(重点)1, 自增主键 六、FOREIGN KEY (重点)1, 插入数据2, 删除数据3, 关于外键约束下删除数据的思考 总结 前言 各位读者好, 我是小陈, 这是我的个人主页, 希望我的专栏能够帮助到你: &#x1f4d5;…

数据库图书管理系统设计报告(基于软件工程)

文章目录 一、实验目的二、实验内容选题方向设计背景第一章 需求分析第一部分 系统设计目标第二部分 功能需求分析1&#xff0e;系统的功能描述2&#xff0e;主要的操作描述3&#xff0e;数据流图的演示&#xff08;1&#xff09;、顶层流程图&#xff08;2&#xff09;、第一层…

JAVA开发(记一次删除完全相同pgSQL数据库记录只保留一条)

进行数据管理时&#xff0c;无效数据可能会对生产力和决策质量造成严重的影响。如何发现和处理无效数据变得愈发重要。一起来唠唠你会如何处理无效数据吧~ 方向一&#xff1a;介绍无效数据的概念 最近遇到了pg数据库表中的大量数据重复了&#xff0c;需要删除其中的一条。一条…

数据库事务基本概念介绍

一、数据库事务是什么&#xff1f; 我们先不说数据库中的事务&#xff0c;我们看下百度对事务这个词汇的解释&#xff1a; 事务&#xff1a; 也就是说&#xff0c;事务就是&#xff0c;要做或者所做的事情 好的我们再联系一下生活中平常做的一些事情 例如 小 x 去 爱存不存的…

快速指南:在CentOS 7上安装Redis,构建高性能键值存储数据库

导航目录 1、Redis安装并使用1.1、下载安装包1.2、重命名和移动文件1.3、redis编译安装1.4、redis启动1.5、设置后台启动redis1.6、设置redis密码1.7、设置redis服务远程访问1.8、redis服务的客户端连接测试1.9、命令行使用1.10、清理缓存 Redis&#xff08;Remote Dictionary …

opencv_c++学习(二十一)

一、图像的轮廓检测 轮廓检测函数&#xff1a; findContours(lnputArray image, OutputArrayOfArrays contours,OutputArray hierarchy, int mode, int method, Point offset Point())image:输入图像&#xff0c;数据类型为CV_8U的单通道灰度图像或者二值化图像。contours:检…

mPython软件使用指南

①软件界面 一、软件界面的介绍 1.模式切换 硬件编程 Python3.6 Jupyter python3.6模式细节补充&#xff08;一般不使用该模式&#xff0c;此处可跳过&#xff09; Python3.6模式的界面 左侧指令分类栏 Python3.6模式的图形化指令分类分为&#xff1a; Python语法基础相关指令&…

Ajax 获取 JSON数据

文章目录 Ajax获取JSON数据 Ajax获取JSON数据 Ajax 全称“Asynchronous JavaScript and XML”&#xff0c;译为“异步 JavaScript 和 XML”&#xff0c;程序员们习惯称之为“阿贾克斯”&#xff0c;通过 Ajax 我们可以异步在服务器与客户端之间传递数据。在 Ajax 中&#xff0…

机器学习期末复习 贝叶斯分类器

先验概率与后验概率 先验概率&#xff1a;对于某一个概率事件&#xff0c;我们都会有基于自己已有的知识&#xff0c;对于这个概率事件会分别以什么概率出现各种结果会有一个预先的估计&#xff0c;而这个估计并未考虑到任何相关因素。 对于分类数据来说&#xff0c;先验概率就…

【lager】日志系统1:允许多个日志源向多个最终用户进行日志记录 windows cmake构建

【xerces】xerces-c-3.2.4 版本的cmake windows vs2022 构建 lager Light-weight Accumulator Gathering Efficiently in Real-time lagerLAGER(实时高效收集的轻量级累加器)是一个可靠的日志系统,旨在允许多个日志源向多个最终用户进行日志记录。该设计文档概述了整个系统的…

Android---APK 瘦身

在 APP 开发过程中&#xff0c;随着业务迭代&#xff0c;apk 体积逐渐变大。项目中累积的无用资源&#xff0c;未压缩的图片资源等&#xff0c;都为 apk 带来了不必要的体积增加。而 APK 的大小会影响应用加载速度、使用的内存量以及消耗的电量。 APK 结构 APK 文件由一个 Zip…

浅析Koa2中控制器

控制器 什么是控制器 拿到路由分配的任务&#xff0c;并执行 路由的功能是根据不同的 url, 来分配不同的任务。 控制器是拿到路由分配的任务并执行&#xff0c;是注册在路由中的中间件。 所以在 koa 中, 控制器也是一个中间件。 为什么要使用控制器 获取HTTP请求参数处理…

article-码垛机器人admas仿真

按照运动学仿真的类似步骤为机器人添加材料、运动副和关节驱动&#xff0c;给机器人手腕末端施加50N最大负载&#xff0c;仿真模型如图5-17。 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AXYQVZPq-1684936426972)(data:image/svgxml;utf8, )] 图…

OpenGL之纹理

文章目录 什么是纹理加载与创建纹理stb_image.h加载并生成纹理 纹理环绕方式纹理过滤多级渐远纹理 纹理单元 什么是纹理 我们已经了解到&#xff0c;我们可以为每个顶点添加颜色来增加图形的细节&#xff0c;从而创建出有趣的图像。但是&#xff0c;如果想让图形看起来更真实&a…

unity制作一款塔防游戏

文章目录 介绍寻路系统怪物生成器制作3种初级炮台、3种升级炮台设置炮台属性选择炮台&#xff0c;添加监听事件炮弹追踪攻击敌人拖动鼠标实现相机视角转换鼠标光标放在cube上变色文字动画 介绍 关键技术&#xff1a; 寻路系统 生成怪物算法 粒子系统 line renderer制作追踪射线…