java定义list_我的Java Web之路59 - Java中的泛型

本系列文章旨在记录和总结自己在Java Web开发之路上的知识点、经验、问题和思考,希望能帮助更多(Java)码农和想成为(Java)码农的人。

目录

  1. 介绍
  2. 再谈Java中的类型
  3. 为什么需要泛型?
  4. Java中的泛型
  5. 泛型类型
  6. 泛型方法
  7. 总结

介绍

还记得我在这篇文章(我的Java Web之路32 - Spring MVC基于注解的控制器)中列举的Handler方法支持的众多返回值类型和注解吗?其中有不少是如下形式的:

  • HttpEntity, ResponseEntity
  • DeferredResult
  • Callable
  • ListenableFuture, java.util.concurrent.CompletionStage, java.util.concurrent.CompletableFuture

还有这篇文章(我的Java Web之路52 - Spring JDBC初步使用)提到 RowMapper 接口是一个泛型接口,使用时是这样的:

new RowMapper() { ... }

还有这篇文章(我的Java Web之路55 - ORM框架(MyBatis)初步使用)中我们定义HouseMapper接口时,其中一个方法的返回值使用了泛型接口List:

List selectAll();

还有这篇文章(我的Java Web之路58 - Spring整合ORM(MyBatis)2)也提到MapperFactoryBean实际上是一个泛型类,使用基于Java配置的方式如下:

@Beanpublic MapperFactoryBean houseMapper() throws Exception { ... }

这些类和接口使用时都在类名或接口名后面添加了一对尖括号括起其他类名的内容,这就是Java中的泛型,本篇文章就粗略介绍一下,让我们对它有个基础的认识,以后遇到它就能够理解了。

再谈Java中的类型

大家都知道,Java语言虽然是一种面向对象的编程语言,但同时也是一种强类型的编程语言,即任何一个变量都需要先声明它的类型之后才能使用它。关于类型和变量的一些知识,大家可以参考这篇文章和这篇文章。

总的来说,Java中的类型有两种,一种是基本类型(英文是primitive types),包括八个:byte、short、int、long、float、double、char、boolean 。

另一种是引用类型(英文是reference),因为引用类型是指向某一个对象/实例的,所以如果某个变量是引用类型的话,通常我们说该变量是某某类的引用,即该引用所指向的对象/实例的类型是某某类 / 接口。

所以,我们把基本类型和各种类 / 接口都统称为类型,即每一个类 / 接口就是一种类型,也就是说我们可以无限扩展类型,因为我们可以定义无限多个类 / 接口。

为什么需要泛型?

解释了Java中的类型之后,我们再来思考一下为什么需要泛型呢?或者说泛型解决了什么问题呢?

我们经常会遇到这种情况,一些代码逻辑(归为算法也未尝不可,就当是广义上的算法吧)实际上与它处理何种类型的数据是无关的。

举个例子,就拿上面我们经常用到的列表(List)这个类/接口所代表的算法来说,它就好像是现实中的火车一样(可能有些不太恰当),一节一节的,想装旅客就装旅客,想装某种货物就装某种货物,因此就有这样的方法:

E get(int n); //获取第N节车厢的东西,可能是旅客,也可能是其他某种货物E set(int n, E element); //将旅客或者其他某种货物装进第N节车厢

当然,列表(List)这个接口还有很多方法,这里只是拿出这两个来举例。可以看到,列表(List)这种算法(实际上是数据结构)独立于各种数据类型,即它可以容纳各种数据类型的数据。

反过来说,假如没有泛型的话,我们就需要为每一种类型设计一种List,比如:

interface 旅客List { 旅客 get(int n); 旅客 set(int n, 旅客 element);}interface 苹果List { 苹果 get(int n); 苹果 set(int n, 苹果 element);}//必要的话,还需要定义其他List

不知道大家有没有发现上述代码的最大问题是什么?

没错,就是重复啊!除了几个地方的数据类型不同,其他代码都是相同的。所以说,泛型本质上就是解决代码重复问题的。有了泛型,你就可以这样定义List:

interface List { T get(int n); T set(int n, T element);}

使用的时候,我们再指定是何种类型的List:

List list_旅客;List list_苹果;

或许你会想到,既然在Java语言中,一切都是Object类的子类(除了那八个基本类型外),我们可以用Object类来设计List啊:

interface List { Object get(int n); Object set(int n, Object element);}

使用的时候,将类型强制转换不就可以了吗:

List list_旅客;list_旅客.set(0, 旅客A);旅客 旅客A = (旅客)list_旅客.get(0);

不过,这样一来我们就需要使用强制类型转换,而强制类型转换是非常不安全的,比如,往往在某个地方将旅客塞进了某个List,而使用的时候却将它强制转换成苹果,这不就出错了吗。特别是Java编译器是不能发现此类错误的,只有在程序运行时才能发现(错误是越早发现越好)。

而使用泛型,我们在定义某个List变量时就可以指定该List是用来装何种类型的数据的,一来Java编译器可以发现此错误,因为它解析代码的时候就能够记住指定的类型啊;二来我们无需使用强制类型转换。

事实上,Java的泛型在底层就是使用Object来实现的,只不过是由Java编译器为我们进行强制类型转换。

综上所述,泛型有如下好处:

  • 消除重复(可以编写独立于类型的算法,即泛型算法);
  • 编译时就能进行类型检测;
  • 无需使用强制类型转换。

Java中的泛型

Java中的泛型分两种:

  • 泛型类型
  • 泛型方法

泛型对应的英文单词是 generic :

adj.一般的; 普通的; 通用的; 无厂家商标的; 无商标的;

n.同“a generic drug";

[例句]Parmesan is a generic term used to describe a family of hard Italian cheeses.

帕尔玛干酪是意大利硬奶酪的通称。

[其他]复数:generics

可以看到,泛型这个译法一方面与 generic 的本义(泛)是相符的,另一方面又兼顾了编程领域的含义(类型)。

泛型的本质是将类型变为一种参数,这就叫做类型的参数化(parameterized over types)。我们可以拿它与普通变量的参数化进行类比,这样有助于我们的理解和记忆:

ebf670a0972f8ae6a264d4f5dfcdc9d3.png

要注意,形参的英文单词是 parameter;实参的英文单词是 argument 。

所以,类型形参就是 type parameter;类型实参就是 type argument 。

泛型类型

泛型类型又包括两种:

  • 泛型类
  • 泛型接口

泛型类的定义只需要在类名之后添加一对尖括号,然后在尖括号中声明若干个类型形参

new RowMapper() { ... }

泛型接口的定义也是类似:

List selectAll();

当然,尖括号中声明的类型形参就可以在该类或接口中使用了,不管是在属性中还是在方法中都可以使用,比如:

@Beanpublic MapperFactoryBean houseMapper() throws Exception { ... }

由于历史原因,泛型是在JDK的某个版本引入的,所以JDK中存在将原来不是泛型的类型,在后来的版本中改造成了泛型类型,而为了与之前的版本兼容,所以原来不是泛型的类型仍然可以使用,于是Java就引入原始类型(Raw Type)的概念。

举个例子,ArrayList原来不是泛型,后来被改造成了泛型:

public class ArrayList extends AbstractList implements List, RandomAccess, Cloneable, java.io.Serializable

所以,不使用泛型方式的ArrayList就叫原始类型,我们仍然可以定义原始类型的变量,甚至将泛型类型的对象赋值给该变量,当然,反过来也可以,如下:

ArrayList arrayList = new ArrayList();ArrayList listOfInt = list;

不过,这种代码极其不安全,大家最好尽量避免,一般IDE都会给出提示,Java编译器在编译时也会给出警告。

类型形参的命名也有一些约定俗成的规定,但你也可以不遵从:

E - Element:表示元素的类型,通常用在表示集合、容器等概念的泛型类 / 接口中。K - Key:表示键的类型,通常用在映射概念的泛型类 / 接口中。N - NumberT - Type:一般化的命名。V - Value:表示值的类型,通常用在映射概念的泛型类 / 接口中。S,U,V 等 - 一般化的命名,有多个类型形参时使用。

泛型方法

泛型方法是指在方法的定义中有自己的类型形参的声明,如果仅仅使用了泛型类型已经声明的形参,那就不算是泛型方法,举一个官方文档中的例子:

public class Util { public static  boolean compare(Pair p1, Pair p2) { return p1.getKey().equals(p2.getKey()) && p1.getValue().equals(p2.getValue()); }}public class Pair { private K key; private V value; public Pair(K key, V value) { this.key = key; this.value = value; } public void setKey(K key) { this.key = key; } public void setValue(V value) { this.value = value; } public K getKey() { return key; } public V getValue() { return value; }}

泛型方法的类型形参的声明是放在方法返回值的前面,也是使用尖括号将若干形参括起来的形式,如上面的 compare() 方法的定义。

实际调用方法时,可以在方法名前写上类型实参,如:

Pair p1 = new Pair<>(1, "apple");Pair p2 = new Pair<>(2, "pear");boolean same = Util.compare(p1, p2);

当然,前辈们已经把Java编译器实现的足够智能,就算你不写类型实参,它也能够从方法实参的类型中推断出来:

Pair p1 = new Pair<>(1, "apple");Pair p2 = new Pair<>(2, "pear");boolean same = Util.compare(p1, p2);

Java编译器的这个功能就叫做类型推断(type inference)。

总结

本篇文章介绍了Java泛型的基本知识,原理上也是很简单的,大家只要把它当做普通类/接口和普通方法即可,只不过是多了一些语法而已。我们只需要记住,泛型主要还是为了

  • 消除重复
  • 和实现类型安全。

当然,泛型还有很多内容,比如:

  • 类型形参可以设定一些边界,比如只允许某个类的子类或父类当做类型实参。
  • 泛型类/接口的继承有些不同,并不是说类型实参有父子关系,它们的泛型类就拥有父子关系,事实是它们没有任何关系。
  • 泛型中的通配符的使用。
  • 类型推断。
  • 类型擦除。
  • 一些最佳实践。
  • 等等。

这些内容以后慢慢介绍。

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

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

相关文章

通过更改透明度使图片为透明

使用AlphaBlend函数 函数功能 该函数用来显示具有指定透明度的图像。函数原型 AlphaBlend(HDC hdcDest,int nXOriginDest,int nYOriginDest,int nWidthDest,int hHeightDest,HDC hdcSrc,int nXOriginSrc,int nYOriginSrc,int nWidthSrc,int nHeightSrc,BLENDFUNCTION blendFunc…

(转)CocoaPods:管理Objective-c 程序中各种第三方开源库关联

在我们的iOS程序中&#xff0c;经常会用到多个第三方的开源库&#xff0c;通常做法是去下载最新版本的开源库&#xff0c;然后拖拽到工程中。 但是&#xff0c;第三方开源库的数量一旦比较多&#xff0c;版本的管理就非常的麻烦。有没有什么办法可以简化对第三方库的管理呢&…

为什么子进程每次执行顺序不一样_看完这篇还不懂Redis的RDB持久化,你来打我...

推荐观看&#xff1a;Redis缓存穿透的终极解决方案&#xff0c;手写布隆过滤器_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili​www.bilibili.comP8架构师串讲&#xff1a;Redis&#xff0c;zookeeper&#xff0c;kafka&#xff0c;Nginx等技术_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili​w…

Spring XD用于数据提取

Spring XD是一个功能强大的工具&#xff0c;它是一组可安装的Spring Boot服务&#xff0c;可以独立运行&#xff0c;在YARN或EC2之上运行。 Spring XD还包括一个管理UI网站和一个用于作业和流管理的命令行工具。 Spring XD是一组功能强大的服务&#xff0c;可与各种数据源一起使…

JDK 9 REPL:入门

会议是聚会Java名人的好地方。 Devoxx France是与Java语言架构师&#xff0c;前同事和老朋友Brian Goetz&#xff08; briangoetz &#xff09;见面的一个机会。 我们谈论了JDK 9&#xff0c;而他全都热衷于REPL。 他提到&#xff0c;尽管Java SE 9中有很多重要功能 &#xff0…

sinaapp mysql连接_手把手教你在新浪云上免费部署自己的网站--连接数据库

看完之后&#xff0c;默认你知道怎么将代码上传到新浪云SAE&#xff0c;并且能够成功运行&#xff0c;连接数据库之前&#xff0c;你必须先创建有一个应用。现在我创建一个名称为sampleone的应用&#xff0c;如下图点击左侧的代码管理&#xff0c;选在右侧创建一个版本然后就会…

7 centos 查看程序文件数量_解析CentOS 7中系统文件与目录管理

LINUXLinux操作系统解析CentOS 7中系统文件与目录管理Linux目录结构Linux目录结构是树形的目录结构根目录所有分区、目录、文件等的位置起点整个树形目录结构中&#xff0c;使用独立的一个“/”表示常见的子目录目录目录名称目录目录名称/root管理员家目录/bin所有用户可执行命…

python动态绘图并保留之前绘图_[转]基于Python实现matplotlib中动态更新图片(交互式绘图)...

最近在研究动态障碍物避障算法&#xff0c;在Python语言进行算法仿真时需要实时显示障碍物和运动物的当前位置和轨迹&#xff0c;利用Anaconda的Python打包集合&#xff0c;在Spyder中使用Python3.5语言和matplotlib实现路径的动态显示和交互式绘图(和Matlab功能类似)。Anacond…

一步一步学Silverlight 2系列(25):综合实例之Live Search

概述 Silverlight 2 Beta 1版本发布了&#xff0c;无论从Runtime还是Tools都给我们带来了很多的惊喜&#xff0c;如支持框架语言Visual Basic, Visual C#, IronRuby, Ironpython&#xff0c;对JSON、Web Service、WCF以及Sockets的支持等一系列新的特性。《一步一步学Silverlig…

gateway中的局部过滤器_Spring Cloud Gateway中的过滤器工厂:重试过滤器

Spring Cloud Gateway基于Spring Boot 2&#xff0c;是Spring Cloud的全新项目&#xff0c;该项目提供了一个构建在Spring 生态之上的API网关。本文基于的Spring Cloud版本为Finchley M9&#xff0c;Spring Cloud Gateway对应的版本为2.0.0.RC1。Spring Cloud Gateway入门一文介…

cocos2d-x for android配置 运行 Sample on Linux OS

1.从http://www.cocos2d-x.org/download下载稳定版 比如cocos2d-x-2.2 2.解压cocos2d-x-2.2.zip,比如本文将其解压到 /opt 目录下 3.运行 android-buildsetup.sh,运行之前需要先设置3个环境变量,如将以下变量写到文件 /etc/profile中 export ANDROID_SDK_ROOT/opt/android-sdk-…

转变馆藏

您是否曾经想替换过HashSet或HashMap使用的equals和hashCode方法&#xff1f; 或者有一个List的一些元素类型伪装成的List相关类型的&#xff1f; 转换集合使这成为可能&#xff0c;并且本文将展示如何实现。 总览 转换集合是LibFX 0.3.0的一项功能&#xff0c;该功能将在今天…

Spring Boot和Swagger UI

我已经一年没有从头开始开发Spring Web应用程序了&#xff0c;如果我不参加QA自动化工程师的培训&#xff0c;那么这段时间甚至会更长。 由于这个原因&#xff0c;我开发了一个示例REST应用程序。 除了Swagger&#xff0c;一切对我来说都很熟悉。 因此&#xff0c;我将描述我在…

mysql5.7.22打不开_windows下mysql-5.7.22-winx64突然启动不了,报错Could not open log file

本文摘自classinstance.cn。windows下mysql-5.7.22-winx64突然启动不了&#xff0c;感觉启动几秒钟后就自己关闭了&#xff0c;看了下启动日志&#xff1a;2019-08-25T10:57:08.389404Z 0 [Warning] option wait_timeout: unsigned value 31536000 adjusted to 21474832019-08-…

把python37添加到环境变量配置_关于在win 10上成功创建一个Django项目时遇到django-admin的手动配置环境变量问题。...

前言初学Python Web 在创建第一个Djang项目的时候出现了很多的问题&#xff0c;今天和大家分享并记录一下这次艰难的历程&#xff01;一、官网下载Python以及安装Django1、Python的下载安装链接&#xff1a;大家最好使用谷歌浏览器&#xff0c;因为翻译的很到位(大家下载最新版…

在Ant中显示路径

在博客文章Java and Ant Properties Refresher和Ant <echoproperties /> Task中 &#xff0c;我写了一篇关于如何了解Ant构建如何看到属性的文章&#xff0c;这有助于更好地理解构建。 通常情况下&#xff0c;在构建过程中看到构建中使用的各种路径也很有价值&#xff0c…

Azure开发者任务之一:解决Azure Storage Emulator初始化失败

初学Windows Azure&#xff1a; 我打算开始学习Windows Azure。我安装了Azure SDK&#xff0c;然后在“Cloud”标签下选择Windows Azure模板&#xff0c;创建了一个项目&#xff0c;然后又创建了一个Web角色。 在aspx文件上&#xff0c;我只添加了一个标签&#xff0c;但是当我…

java运行python3_python写脚本并用java调用python(三)

1)编写mytest.py完成一个简单加法计算# coding:utf8#def 表示一个方法 adderdef adder(a, b):return ab#这里执行adder方法并打印出结果print adder(1,2)2)运行以上脚本方式如图12 3 打印成功&#xff01;3)java调用python脚本的两种方式Process process Runtime.getRuntime(…

mysql单源多表同步单库单表_MySQL主从复制单表或者多表

MySQL数据库安装不过多的介绍了&#xff1a;必须保证2个数据库的版本一致。 主数据库&#xff1a;192.168.0.43 从数据库&#xff1a;192.168.0.53 修改43主数据MySQL数据库安装不过多的介绍了&#xff1a;必须保证2个数据库的版本一致。主数据库&#xff1a;192.168.0.43从数据…

xshell 秘钥连接_如何使用PuTTY和xshell 分别远程连接linux,并配置秘钥认证

使用PuTTY 连接并配置密钥认证第一步&#xff1a;下载PuTTY下载 .zip 64位的电脑 32位的putty也能用第二步&#xff1a;配置基本信息打开 PuTTY端口默认是22 (端口是可以改的)ip 地址如果忘记&#xff0c;ifconfig 查看一下Load >Open输入登录名 密码即可完成登录若出现上…