Java泛型

使用泛型机制编写的程序代码要比那些杂乱地使用Object变量,然后再进行强制类型转换的代码具有更好的安全性和可读性。

一、为什么要使用泛型程序设计

1、增加泛型类之前

Java中增加泛型类之前,泛型程序设计是用继承实现的。

    比如下面的代码中,ArrayList类只维护一个Object引用的数组:

    这样的实现有两个问题:

        ①当获取一个值时必须进行强制类型转换。

        ②这里没有错误检查,可以向数组列表中添加任何类的对象。

没有错误检查,可能导致的问题:如上所示,将一个File对象放进了容器中(编译和运行都不会出错),之后取出来强转成String类型,就会报错。

2、增加泛型类之后

二、定义简单泛型类

    要知道泛型类的本质,使用泛型类就是为了编写的代码可以被很多不同类型的对象所复用,例如我们并不希望为聚集(容器)StringFile对象分别设计不同的类,所以设计一个泛型类ArrayList<T>,泛型类就相当于一个模板类,可以适用于很多类(不要被这个T束缚,首先要明白泛型类是一个类,这里我们想要一个容器类,只是想让这个容器可以装很多不同的类的对象,所以就用T来代表被装的不同的类,泛型类最关键的是要明白它本身要干什么T只是一个参数;还有一点,这个ArrayList里面保持了一个数组或者啥的也是一个迷惑点,泛型类不是代表它要装多少个对象,而是取决于它要干什么;比如我们可以设计一个泛型类Add<T>,它是想要将两个对象相加,那么T可以是不同类型的。。。)

    一个泛型类就是具有一个或多个类型变量的类,比如下面的代码:

    用具体的类型替换类型变量后,可以将得到的Pair<String>想象成一个普通类:

具有构造器:

具有方法:

        换句话说,泛型类可以看作普通类的工厂。

三、泛型方法

    前面已经介绍了如何定义一个泛型类。实际上,还可以定义一个带有类型参数的简单方法:

    这个方法是在普通类中定义的,然而,这是一个泛型方法(可以从尖括号和类型变量看出这一点)。注意,类型变量是放在修饰符(public static)的后面,返回类型的前面。

    泛型方法可以定义在普通类中,也可以定义在泛型类中。

四、类型变量的限定

    有时,类或方法需要对类型变量加以约束限定(就是规定类型变量只能是哪些类)。

    下面的例子,要找出数组中的最小元素:

五、泛型代码和虚拟机

    虚拟机中没有泛型类型对象,即所有对象都属于普通类。

    无论何时定义一个泛型类型,都自动提供了一个相应的原始类型(raw type)。原始类型的名字就是删除类型参数名和尖括号后的泛型类型名。将泛型类型的代码变成原始类型的代码:其它保持不变,改变类名,将类型参数(比如T)做修改,如果T有限定类型,就将T换成T第一个限定的类型变量;如果T没有限定类型,就将T换成Object。比如:

Pair<T>的代码:

    Pair<T>的原始类型代码:

    在程序中可以包含不同类型的Pair,例如,Pair<String>Pair<Date>,而擦除类型后就变成原始的Pair类型了。

    另一个关于转换原始类型的例子:

1、翻译泛型表达式

虚拟机中没有泛型类型对象,即所有对象都属于普通类。因为Java程序是运行在Java虚拟机中的,所以运行时会有类型擦除。

注意:在运行时,代码的类型被擦除,但对象的类型不会被擦除。所谓被擦除,是指Java虚拟机将运行的是擦除后生成的一份字节码。

2、翻译泛型方法

注意:运行时的类型擦除是把泛型类的所有代码都擦除成了原始类型,所以只要涉及到泛型代码,思考运行时都是想着把代码擦除,然后只不过编译器会在运行时,将方法调用的代码翻译成虚拟机指令的过程中,又把类型强转回来。

所以在DateInterval类中就存在两个setSecond()方法,一个的类型参数是Date,一个的类型参数是Object。(在运行时,因为擦除,上面的重写实际上变成了重载,而我们想要达到的目的是重写【重写要求子类和父类的方法名相同,方法类型相同,extends Pair<Date>,用Date实例化了T,所以重写时就想的是把父类的setSecond(T second)T也用Date来实例化,但是达不到目的,见下面】)

考虑下面的语句序列:

由于Pair<Date>类型的引用pair指向了一个DateInterval类型的对象(父类引用指向了子类对象),所以pair.setSecond应该调用子类的setSecond方法,按照多态的想法,想调用的应该是子类重写父类的那个方法,pair因为是父类Pair<Date>类型的(这里的子类是DateInterval,子类对象是interval),它要调用的setSecond()只能是父类定义的(多态的基本原理),【我们所想的:setSecond(T a)setSecond(Date a)应该是属于重写的范畴,所以我们认为父类Pair<T>类型的pair指向一个子类对象时,调用setSecond会调用子类对象的setSecond(Date a)】,但是不是这样的,由于类型擦除,运行代码时,Java虚拟机进行擦除,生成一份新的擦除后的代码,运行的就是这份新的代码,父类的setSecond(T a)被擦除成了setSecond(Object a),这时候子类没有重写父类的setSecond(Object a)方法(因为没擦除前父类也没有),所以只能调用子类从父类继承下来的被擦除后的setSecond(Object a)方法,吊用不了重写的setSecond(Date a),相当于是重载了,就达不到目的。那么应该如何解决呢?

要解决这个问题,就需要编译器DateInterval类中生成一个桥方法(bridge method):

3、调用遗留代码

    会有警告是因为:方法setLabelTable的参数类型是Dictionary,而不是Dictionary<Integer,Component>,而我们这里给使用泛型就是因为这个泛型类里的部分属性的类型是类型参数所表示的类型,而方法中没使用泛型,方法就会将传入的泛型类型的对象的那些带类型参数的属性的类型视为Object,所以这就警告我们不要在方法体中进行不当的操作。

六、约束和局限性

其实所有这些局限性,擦除啥的都是为了防止类型转换上的错误,并不影响实际的泛型使用,因为编译器会生成强制类型转换的指令。

1、不能用基本类型实例化类型参数

2、运行时类型查询只适用于原始类型

3、不能创建参数化类型的数组

首先,关于Java的数组:

Java中,任何一个数组都可以向上转型成在定义时指定元素类型的父类的数组,如果在将数组向上转型后,我们往数组里放不同于原始数据类型(即数组定义时的类型)的元素,但是元素的类型满足后来使用的父类类型(即放入的元素的类型是向上转型后的类型的子类)的话,编译不会有问题,但是在运行时会检查加入数组的对象的类型,于是会抛ArrayStoreException(但是若还是放定义时的类型,不会报错):

 

再看Java的泛型:

Java对泛型的支持其实就是在编译器中做了做手脚,增加了一些强制类型转换的代码,也就是说原来需要我们手动写的一些强制类型转换的代码,在泛型的世界里,Java编译器就帮我们做了。

Java的范型停留在编译这一层,到了运行时,这些范型的信息其实是被抹掉的。Java的做法不必修改JVM,减少了潜在的大幅改动和随之而来的风险,也许同时也反映出Java Bytecode规范在设计之初的先天不足。

 

为什么Java不支持创建泛型数组:

因为这样做会破坏类型安全。

因为Java的范型会在运行时将类型信息抹掉,这样如果Java允许我们使用类似

这样的语句的话,我们在随后的代码中可以把它转型为Object[]然后往里面放Map<Double, String>的实例。这样做不但编译器不能发现类型错误,就连运行时的数组存储检查对它也无能为力,它能看到的是我们往里面放Map的对象,我们定义的<Integer, String>在这个时候已经被抹掉了,于是而对它而言,只要是Map,都是合法的。想想看,我们本来定义的是装Map<Integer, String>的数组,结果我们却可以往里面放任何Map,接下来如果有代码试图按原有的定义去取值,后果是什么不言自明。(泛型是为了消灭ClassCastException而出现的,但是在这个时候它自己却可能引发ClassCastException,所以就禁止它)

所以,Java编译器不允许我们new泛型数组。

 

另一个例子:

根据上面的分析可以看出来,泛型其实是挺严谨的,说白了就是在"编译的时候通过增加强制类型转换的代码,来避免用户编写出可能引发ClassCastException的代码"。这其实也算是Java引入泛型的一个目的。

但是,一个颇具讽刺意味的问题出现了:如果允许了泛型数组,那么编译器添加的强制类型转换的代码就会有可能是错误的。

看下面的例子:

    上面的代码中,最后一行是重点。因为Java泛型,在编译时通过增加强制类型转换的代码,所以"String value = ref.getValue()"会被替换成"String value = (String)ref.getValue()"。当然我们知道,ref实际上是指向一个存储着StringBuffer对象的GenTest对象。所以,编译器生成出来的代码是隐含着错误的,在运行的时候就会抛出ClassCastException

从这里可以看出,即使支持泛型数组也不会带来什么灾难性的后果,最多就是可能引发ClassCastException。而且平心而论,这个还是程序员自己的错误,实在算不得是Java编译器的错误。但是从另一个角度看,这确实是个巨大的讽刺:泛型是为了消灭ClassCastException而出现的,但是在这个时候它自己却引发了ClassCastException

但是List<String> a = new ArrayList<String>(); 是可以的,因为虽然List是一个容器,但它本质上还是一个类,涉及不到数组的那些转换什么的。

 

    需要说明的是,声明类型为Pair<String>[]的变量仍是合法的,不过不能用new Pair<String>[10]来初始化这个数组变量,即不允许创建(即不能去new)参数化类型的数组(泛型数组)。

4、Varargs警告

上图中的情况也是为什么"需要说明的是,声明类型为Pair<String>[]变量仍是合法的,不过不能用new Pair<String>[10]来初始化这个数组变量,即不允许创建(即不能去new)参数化类型的数组(泛型数组)。"可以声明类型为Pair<String>[]的变量的原因,就是为了在这里使用,又给程序员留了一条小路,但是程序员要严格注意类型转换上的问题。

5、不能实例化类型变量

 

多源动态最优潮流的分布鲁棒优化方法(IEEE118节点)(Matlab代码实现)内容概要:本文介绍了基于Matlab实现的多源动态最优潮流的分布鲁棒优化方法,适用于IEEE118节点电力系统。该方法旨在应对电力系统中源荷确定性带来的挑战,通过构建分布鲁棒优化模,有效处理多源输入下的动态最优潮流问题,提升系统运行的安全性和经济性。文中详细阐述了模的数学 formulation、求解算法及仿真验证过程,并提供了完整的Matlab代码实现,便于读者复现与应用。该研究属于电力系统优化调度领域的高水平技术复现,具有较强的工程实用价值。; 适合人群:具备电力系统基础知识和Matlab编程能力的研究生、科研人员及从事电力系统优化调度的工程技术人员,尤其适合致力于智能电网、鲁棒优化、能源调度等领域研究的专业人士。; 使用场景及目标:①用于电力系统多源环境下动态最优潮流的建模与求解;②支撑含可再生能源接入的电网调度决策;③作为鲁棒优化方法在实际电力系统中应用的教学与科研案例;④为IEEE118节点系统的仿真研究提供可复现的技术支持。; 阅读建议:建议结合提供的Matlab代码逐模块分析,重点关注确定变量的分布鲁棒建模、目标函数构造及求解器调用方式。读者应具备一定的凸优化和电力系统分析基础,推荐配合YALMIP工具包与主流求解器(如CPLEX、Gurobi)进行调试与扩展实验。
内容概要:本文系统介绍了物联网与云计算的基本概念、发展历程、技术架构、应用场景及产业生态。文章阐述了物联网作为未来互联网的重要组成部分,通过RFID、传感器网络、M2M通信等技术实现物理世界与虚拟世界的深度融合,并展示了其在智能交通、医疗保健、能源管理、环境监测等多个领域的实际应用案例。同时,文章强调云计算作为物联网的支撑平台,能够有效应对海量数据处理、资源弹性调度和绿色节能等挑战,推动物联网规模化发展。文中还详细分析了物联网的体系结构、标准化进展(如IEEE 1888、ITU-T、ISO/IEC等)、关键技术(中间件、QoS、路由协议)以及中国运营商在M2M业务中的实践。; 适合人群:从事物联网、云计算、通信网络及相关信息技术领域的研究人员、工程师、高校师生以及政策制定者。; 使用场景及目标:①了解物联网与云计算的技术融合路径及其在各行业的落地模式;②掌握物联网体系结构、标准协议与关键技术实现;③为智慧城市、工业互联网、智能物流等应用提供技术参考与方案设计依据;④指导企业和政府在物联网战略布局中的技术选与生态构建。; 阅读建议:本文内容详实、覆盖面广,建议结合具体应用场景深入研读,关注技术标准与产业协同发展趋势,同时结合云计算平台实践,理解其对物联网数据处理与服务能力的支撑作用。
标题基于Java的停车场管理系统设计与实现研究AI更换标题第1章引言介绍停车场管理系统研究背景、意义,分析国内外现状,阐述论文方法与创新点。1.1研究背景与意义分析传统停车场管理问题,说明基于Java系统开发的重要性。1.2国内外研究现状综述国内外停车场管理系统的发展现状及技术特点。1.3研究方法以及创新点介绍本文采用的研究方法以及系统开发中的创新点。第2章相关理论总结Java技术及停车场管理相关理论,为系统开发奠定基础。2.1Java编程语言特性阐述Java的面向对象、跨平台等特性及其在系统开发中的应用。2.2数据库管理理论介绍数据库设计原则、SQL语言及在系统中的数据存储与管理。2.3软件工程理论说明软件开发生命周期、设计模式在系统开发中的运用。第3章基于Java的停车场管理系统设计详细介绍系统的整体架构、功能模块及数据库设计方案。3.1系统架构设计阐述系统的层次结构、模块划分及模块间交互方式。3.2功能模块设计介绍车辆进出管理、车位管理、计费管理等核心功能模块设计。3.3数据库设计给出数据库表结构、字段设计及数据关系图。第4章系统实现与测试系统实现过程,包括开发环境、关键代码及测试方法。4.1开发环境与工具介绍系统开发所使用的Java开发环境、数据库管理系统等工具。4.2关键代码实现展示系统核心功能的部分关键代码及实现逻辑。4.3系统测试方法与结果阐述系统测试方法,包括单元测试、集成测试等,并展示测试结果。第5章研究结果与分析呈现系统运行效果,分析系统性能、稳定性及用户满意度。5.1系统运行效果展示通过截图或视频展示系统实际操作流程及界面效果。5.2系统性能分析从响应时间、吞吐量等指标分析系统性能。5.3用户满意度调查通过问卷调查等方式收集用户反馈,分析用户满意度。第6章结论与展望总结研究成果,提出系统改进方向及未来发展趋势。6.1研究结论概括基于Java的停车场管理
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值