码出高效:Java开发手册-第2章(11)

简介: 本章开始讲解面向对象思想,并以Java 为载体讲述面向对象思想在具体编程语言中的运用与实践。当前主流的编程语言有50 种左右,主要分为两大阵营:面向对象编程与面向过程编程。面向对象编程(Object-Oriented Programming,OOP)是划时代的编程思想变革,推动了高级语言的快速发展和工业化进程。OOP 的抽象、封装、继承、多态的理念使软件大规模化成为可能,有效地降低了软件开发成本、维护成本和复用成本。面向对象编程思想完全不同于传统的面向过程编程思想,使大型软件的开发就像搭积木一样隔离可控、高效简单,是当今编程领域的一股势不可......

2.6 泛型

      泛型的本质是类型参数化,解决不确定具体对象类型的问题。在面向对象编程语言中,允许程序员在强类型校验下定义某些可变部分,以达到代码复用的目的。泛型(generic)、天才(genius)、基因(gene)三个英文单词的词根都是gen,最神奇的是,它们无论是拼写还是发音都十分相像,在沟通中往往比较含糊。可以这样理解,泛型就是这些拥有天才基因的大师们发明的。

      Java 在引入泛型前,表示可变类型,往往存在类型安全的风险。举一个生活中的例子,微波炉最主要的功能是加热食物,即加热肉、加热汤都有可能。在没有泛型的场景中,往往会写出:

class Stove {

      public static Object heat(Object food) {

             System.out.println(food + "is done");

             return food;

      }

      public static void main(String[] args) {

             Meat meat = new Meat();

             meat = (Meat)Stove.heat(meat);

             Soup soup = new Soup();

             soup = (Soup)Stove.heat(soup);

      }

}

      为了避免给每种食材定义一个加热方法,如heatMeat()、heatSoup() 等,将heat()的参数和返回值定义为Object,用“向上转型”的方式,让其具备可以加热任意类型对象的能力。这种方式增强了类的灵活性,但却会让客户端产生困惑,因为客户端对加热的内容一无所知,在取出来时进行强制转换就会存在类型转换风险。泛型则可以完美地解决这个问题。

      泛型可以定义在类、接口、方法中,编译器通过识别尖括号和尖括号内的字母来解析泛型。在泛型定义时,约定俗成的符号包括:E 代表Element,用于集合中的元素;T 代表the Type of object,表示某个类;K 代表Key、V 代表Value,用于键值对元素。我们用一个示例彻底地记住泛型定义的概念,对泛型不再有恐惧心理。如果下面代码编译出错,请指出编译出错的位置在哪里:

public class GenericDefinitionDemo<T> {

      static <String, T, Alibaba> String get(String string, Alibaba alibaba) {

             return string;

      }

      public static void main(String[] args) {

             Integer first = 222;

             Long second = 333L;

             // 调用上方定义的get 方法

             Integer result = get(first, second);

      }

}

      事实上,以上代码编译正确且能够正常运行,get() 是一个泛型方法,first 并非是

java.lang.String 类型,而是泛型标识<String>,second 指代 Alibaba。get() 中其他有被用到的泛型符号并不会导致编译出错,类名后的T 与尖括号内的T 相同也是合法的。当然在实际应用时,并不会存在这样的定义方式,这里只是期望能够对以下几点加深理解:

(1)尖括号里的每个元素都指代一种未知类型。String 出现在尖括号里,它就不是java.lang.String,而仅仅是一个代号。类名后方定义的泛型<T> 和get() 前方定义的<T> 是两个指代,可以完全不同,互不影响。

(2)尖括号的位置非常讲究,必须在类名之后或方法返回值之前。

(3)泛型在定义处只具备执行Object 方法的能力。因此想在get() 内部执行string.longValue() + alibaba.intValue() 是做不到的,此时泛型只能调用Object 类中的方法,如toString()。

(4)对于编译之后的字节码指令,其实没有这些花头花脑的方法签名,充分说明了泛型只是一种编写代码时的语法检查。在使用泛型元素时,会执行强制类型转换:

INVOKESTATIC com/alibaba/easy/coding/generic/GenericDefinitionDemo.get

(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;

CHECKCAST java/lang/Integer

      这就是坊间盛传的类型擦除。CHECKCAST 指令在运行时会检查对象实例的类型是否匹配,如果不匹配,则抛出运行时异常ClassCastException。与C++ 根据模板类生成不同的类的方式不同,Java 使用的是类型擦除的方式。编译后,get() 的参数是两个Object,返回值也是Object,尖括号里很多内容消失了,参数中也没有String 和Alibaba 两个类型。数据返回给Integer result 时,进行了类型强制转化。因此,泛型就是在编译期增加了一道检查而已,目的是促使程序员在使用泛型时安全放置和使用数据。使用泛型的好处包括:

  • 类型安全。放置的是什么,取出来的自然是什么,不用担心会抛出ClassCastException 异常。
  • 提升可读性。从编码阶段就显式地知道泛型集合、泛型方法等处理的对象类型是什么。
  • 代码重用。泛型合并了同类型的处理代码,使代码重用度变高。

      回到本节开头微波炉加热食材的例子,使用泛型可以很好地实现,示例代码如下:

public class Stove {

      public static <T> T heat(T food) {

             System.out.println(food + "is done");

             return food;

      }

      public static void main(String[] args) {

             Meat meat = new Meat();

             meat = Stove.heat(meat);

             Soup soup = new Soup();

             soup = Stove.heat(soup);

      }

}

      通过使用泛型,既可以避免对加热肉和加热汤定义两种不同的方法,也可以避免使用Object 作为输入和输出,带来强制转换的风险。只要这种强制转换的风险存在,依据墨菲定律,就一定会发生ClassCastException 异常。特别是在复杂的代码逻辑中,会形成网状的调用关系,如果任意使用强制转换,无论可读性还是安全性都存在问题。

      最后,泛型与集合的联合使用,可以把泛型的功能发挥到极致,很多程序员不清楚List、List<Object>、List<?> 三者的区别, 更加不能区分<? extends T> 与<? super T> 的使用场景。具体请参考第6.5 节。

相关文章
|
15天前
|
监控 JavaScript 前端开发
《理解 WebSocket:Java Web 开发的实时通信技术》
【4月更文挑战第4天】WebSocket是Java Web实时通信的关键技术,提供双向持久连接,实现低延迟、高效率的实时交互。适用于聊天应用、在线游戏、数据监控和即时通知。开发涉及服务器端实现、客户端连接及数据协议定义,注意安全、错误处理、性能和兼容性。随着实时应用需求增加,WebSocket在Java Web开发中的地位将更加重要。
|
2月前
|
NoSQL Java 关系型数据库
凭借Java开发进阶面试秘籍(核心版)逆流而上
最近参加了面试或者身边有朋友在面试的兄弟有没有发现,现在的面试不仅会问八股文,还会考察框架、项目实战、算法数据结构等等,需要准备的越来越多。 其实面试的时候,并不是要求你所有的知识点都会,而是关键的问题答到点子上!这份《Java 开发进阶面试秘籍(核心版)》由 P8 面试官整体把控,目前已经更新了 30 万字! 资料中涵盖了一线大厂、中小厂面试真题,毕竟真题都是技术领域最经典的基础知识和经验沉淀的汇总,非常有必要学习掌握!双重 buff 叠加,offer 接到手软~ 点击此处取,这可能是你到目前为止领取的最具含金量的一份资料! 整套资料涵盖:Spring、Spring
|
1月前
JavaWeb 开发之 ServletContext 的和使用
JavaWeb 开发之 ServletContext 的和使用
21 1
|
1天前
|
前端开发 Java Go
开发语言详解(python、java、Go(Golong)。。。。)
开发语言详解(python、java、Go(Golong)。。。。)
|
1天前
|
人工智能 前端开发 Java
Java语言开发的AI智慧导诊系统源码springboot+redis 3D互联网智导诊系统源码
智慧导诊解决盲目就诊问题,减轻分诊工作压力。降低挂错号比例,优化就诊流程,有效提高线上线下医疗机构接诊效率。可通过人体画像选择症状部位,了解对应病症信息和推荐就医科室。
27 10
|
1天前
|
Java 关系型数据库 MySQL
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
UWB (ULTRA WIDE BAND, UWB) 技术是一种无线载波通讯技术,它不采用正弦载波,而是利用纳秒级的非正弦波窄脉冲传输数据,因此其所占的频谱范围很宽。一套UWB精确定位系统,最高定位精度可达10cm,具有高精度,高动态,高容量,低功耗的应用。
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
|
8天前
|
运维 NoSQL 算法
Java开发-深入理解Redis Cluster的工作原理
综上所述,Redis Cluster通过数据分片、节点发现、主从复制、数据迁移、故障检测和客户端路由等机制,实现了一个分布式的、高可用的Redis解决方案。它允许数据分布在多个节点上,提供了自动故障转移和读写分离的功能,适用于需要大规模、高性能、高可用性的应用场景。
15 0
|
10天前
|
人工智能 小程序 Java
JAVA开发智慧学校系统源码+人脸电子班牌布局
智慧校园是通过利用物联网,大数据技术来改变师生和校园资源相互交互的方式,以便提高交互的明确性、灵活性和响应速度,从而实现智慧化服务和管理的校园模式。
|
16天前
|
XML JSON JavaScript
使用JSON和XML:数据交换格式在Java Web开发中的应用
【4月更文挑战第3天】本文比较了JSON和XML在Java Web开发中的应用。JSON是一种轻量级、易读的数据交换格式,适合快速解析和节省空间,常用于API和Web服务。XML则提供更强的灵活性和数据描述能力,适合复杂数据结构。Java有Jackson和Gson等库处理JSON,JAXB和DOM/SAX处理XML。选择格式需根据应用场景和需求。
|
16天前
|
前端开发 Java API
构建RESTful API:Java中的RESTful服务开发
【4月更文挑战第3天】本文介绍了在Java环境中构建RESTful API的重要性及方法。遵循REST原则,利用HTTP方法处理资源,实现CRUD操作。在Java中,常用框架如Spring MVC简化了RESTful服务开发,包括定义资源、设计表示层、实现CRUD、考虑安全性、文档和测试。通过Spring MVC示例展示了创建RESTful服务的步骤,强调了其在现代Web服务开发中的关键角色,有助于提升互操作性和用户体验。
构建RESTful API:Java中的RESTful服务开发