java泛型 深入理解Java泛型

当前位置:首页 > 军事

java泛型 深入理解Java泛型

发布时间:2020-12-22 07:49:58

>。>。>。>。

什么是泛型?

说到泛型,大家当然不会陌生。在我们的代码中有许多这样的语句:

ArrayList是一个泛型类。通过设置不同的类型,我们可以在集合中存储不同的数据类型(而且只能存储设置的数据类型,这是泛型的优点之一)。“泛型”仅仅意味着泛型类型(参数化类型)。想象一下这个场景:如果我们要写一个容器类(支持数据添加和删除查询),我们就写一个支持String类型的,然后我们需要写一个支持Integer类型的。那么什么是Doubel,Float,各种自定义类型呢?重复代码太多,这些容器的算法都是一致的。我们可以通过引用一个类型T来替换之前需要的所有类型,并将我们需要的类型作为参数传递到容器中,这样我们的算法只需要编写一个集合就可以适应所有类型。最典型的例子是ArrayList,无论我们传递什么数据类型,它都能很好地工作。

看了上面的描述,聪明的同学灵机一动,写下了下面的代码:

这段代码非常灵活,所有类型都可以转换成Object类,这样我们就可以在其中存储各种类型的数据。的确,Java在泛型出现之前也是这样做的。但是有一个问题:如果集合中有很多数据,那么某个数据转换就有错误,在编译时找不到。但是,在运行时,会出现Java . lang . ClassCasteException。例如:

我们在这个集合中存储多种类型(在某些情况下,容器可能存储多种类型的数据)。如果数据量很大,那么在转换过程中必然会出现异常,这在编译时是无法得知的。一方面,泛型允许我们只向集合中添加一种类型的数据,同时,我们可以在编译时发现这些错误,避免运行时异常,并提高代码的健壮性。

>。>。>。>。

Java泛型介绍

先介绍一下Java泛型的相关内容,下面会介绍以下几个方面:

Java泛型类Java泛型方法Java泛型接口Java泛型擦除及其相关内容Java泛型通配符

Java泛型类

类结构是面向对象中最基本的元素。如果我们的类需要良好的可扩展性,我们可以将其设置为泛型。假设我们需要一个数据包装类,它可以通过传入不同类型的数据来存储相应类型的数据。让我们看看这个简单泛型类的设计:

定义泛型类时,只需在类名后添加一个类型参数。当然,您也可以添加多个参数,类似于

泛型类最常见的使用场景是元组的使用。我们知道方法return的返回值只能返回单个对象。如果我们定义一个泛型类,定义2个甚至3个类型参数,当我们返回一个对象时,我们构造这样一个“元组”数据,通过泛型传入多个对象,这样我们就可以一次接近多个数据。

Java泛型方法

我们之前介绍的泛型在整个类中进行了工作。现在我们来介绍一下泛型方法。泛型方法可以存在于泛型类和普通类中。如果泛型方法可以解决问题,那么就要尽可能多地使用。让我们通过例子来看看泛型方法的使用:

让我们看看运行结果:

从上面的例子中,我们可以看到我们在一个泛型类中定义了一个泛型方法printInfo。通过导入不同的数据类型,我们可以将它们打印出来。在这个方法中,我们定义了类型参数e。在泛型类中,这个e和t之间没有关系。即使我们这样设置通用方法:

这个泛型方法仍然可以传入Double、Float和其他类型的数据。泛型方法中的类型参数t不同于泛型类中的类型参数。从上面的调用模式中,我们还可以看到泛型方法printInfo不受我们的DataHolder中泛型类型参数String的影响。让我们总结一下泛型方法的几个基本特征:

public与返回值中间非常重要,可以理解为声明此方法为泛型方法。只有声明了的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。

Java通用接口

Java泛型接口的定义和Java泛型类的定义基本相同。这里有一个例子:

这里有两点需要注意:

当泛型接口没有传入泛型参数时,它与泛型类的定义相同。声明类时,有必要将泛型声明添加到类中。例子如下:

如果泛型接口的实现类是在泛型接口传入类型参数时实现的,则所有使用泛型的地方都应该由传入的参数类型替换。例子如下:

从这个例子可以看出,类中所有实现T的地方都需要实现为String。

Java通用擦除及其相关内容

让我们看看下面的例子:

extend关键字后的类型信息决定了泛型参数可以保留的信息。擦除Java类型只会擦除HasF类型。

Java通用擦除原理

让我们举个例子,首先看一个非泛型版本:

我们能做些什么来补救呢?以下是解决以上问题的几种方法。

类型判断问题

我们可以用下面的代码来解决因为擦除而无法判断泛型的类型信息的问题:

在主方法中,我们可以调用:

我们记录类型参数的Class对象,然后通过这个Class对象判断类型。

创建类型实例

在泛型代码中找不到新的T()有两个原因。一种是因为擦除无法确定类型。无法确定t是否包含无参数构造函数。

为了避免这两个问题,我们使用显式工厂模式:

创建通用数组

通常不建议创建通用数组。请尝试使用ArrayList而不是泛型数组。但是这里有一个创建泛型数组的方法。

现在我们定义一个板块类:

下面,我们定义一个果盘。理论上,苹果可以存在于果盘中。

你会发现这段代码无法编译。苹果盘不能转换成水果盘:

从上面的代码中我们知道,即使容器中的类型之间存在继承关系,Plate和Plate之间也不存在继承关系。在这种情况下,Java被设计为板

牌照<。?扩展水果。是盘子

让我们通过一个更详细的例子来看看上限:

在上面的类层次结构中,板块

你会发现你不能把数据设置进去,所以说我们把泛型设置为?扩展水果.理所当然,我们应该可以添加水果的子类。但是Java编译器不允许这样。& lt?扩展水果。会使把东西放到盘子里的set()方法失效。但是get()方法仍然有效

原因是:

Java编译时只知道水果及其派生类存储在容器中,不知道是什么类型,可能是水果。可能是苹果?还是香蕉,红苹果,绿苹果?编译器看到板

但是,上限通配符是允许读取的。例如代码:

这个我们很能理解。因为上面的通配符设置容器只能存储水果及其派生类,所以我们可以隐式地将获得的转换为它们的基类(或Object基类)。因此,上限描述符Extends适用于频繁读取的场景。

下限通配符

下限通配符意味着容器中只能存储T及其基类类型的数据。我们还是看上面的班级水平,

下限通配符

原因是:

较低的通配符指定了元素的最小粒度,必须是T及其基类,所以我可以在其中存储T及其派生类,因为可以隐式转换为T类型。但是读出的时候很难控制。里面存储的都是T及其基类,不能转换成任何类型,只能安装Object基类。

PECS原理

最后简单介绍一下《有效Java》一书中介绍的PECS原理。

上界

下界

& lt?>。无限通配符

无界通配符意味着可以使用任何对象,因此使用它们类似于使用本机类型。但是有用。本机类型可以保存任何类型,而由无界通配符修改的容器保存特定类型。例如,在“列表”类型的引用中,不能向其中添加对象,而“列表”类型的引用可以添加“对象”类型的变量。

最后提醒一下,List和List不一样,List是List的子类。你不能去名单

链接:https://juejin.im/post/5b614848e51d45355d51f792

欢迎分享转载 →java泛型 深入理解Java泛型

Copyright © 2002-2020 鲁旭娱乐网 版权所有 备案号:粤ICP备14025430号-1

收藏本站 - 网站地图 - 关于我们 - 网站公告 - 广告服务