Java泛型详解

Java知识点总结:想看的可以从这里进入

10、泛型


10.1、概念

在泛型没有引入的时候,Java有一个缺点,那就是把对象保存到集合中后,集合不会记住保存数据的类型,当后续再次使用时,这些数据的编译类型会变成Object。这样在使用时,往往都需要进行类型转换,这样不仅增加了复杂程度,在转换时还容易出现ClassCastException异常。

所以JDK1.5版本后引入了泛型这个概念。泛型的本质是把元素的类型设计成一个参数,允许程序在传递参数时指定传递的类型。它就像是一个标签(比如超市的货柜上贴上标签后,该位置就只放和标签对应的商品),泛型就是告诉我们传递进去的数据是什么类型的,并且会对传递的参数进行约束,这样就能将参数进行统一,再获取时就不会出现ClassCastException异常。

泛型允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型,这个类型参数将在使用时是确定的。

泛型最大的好处有两点:
1、使用泛型能让我们在使用前就可以明确知道传递或返回的数据类型
2、泛型具有限制作用,一旦规定了一种数据类型,就只会接收这一种数据类型

10.2、泛型的使用

10.2.1、在集合中使用

比方说:我们使用ArrayList时,没有泛型。我们甚至不知道会保存进去什么类型的参数,这样使用起来显然很不方便。大家想想,我们获取的数据是什么类型的都不知道,那我们后续要怎么样去处理数据呢?

image-20230218150225067

而在集合中添加了泛型对传递的数据进行约束后,我们在后续获取中就能明确的知道该集合保存的数据类型了,只需要在类后面添加一个<>,在<>内注明要传递的类型即可。

image-20230218150542667
10.2.2、自定义泛型

泛型允许在定义类、接口、方法时使用,它将在声明变量、创建对象、调用方法时动态地指定(即传入实际的类型参数,也可称为类型实参)。<>中的字母是通配符。但是静态方法、静态初始化块或者静态变量的声明和初始化中不允许使用泛型形参。

image-20230218153057433

<>中的字母在Java中常用的有:?, TK, V, E

  1. ?表示不确定的Java类型(通配符)

  2. Ttype, 表示一种具体的Java类型

  3. Kkey, 表示Java键值对中的键

  4. V取词为value, 表示Java键值对中的值

    image-20230218153518014
  5. E取词为element, 表示元素

    image-20230218153541429

比如使用 List传递的泛型定位 时,可以想象成 E被全部替换成String了。这样通过一个List接口生成无数个不同参数类型的 List 接口。

创建泛型的注意事项:

1、泛型的<>内是能有多个参数的

2、泛型类的构造器不能带<>

3、泛型不同的引用不能相互赋值。ArrayList和ArrayList虽然语法上说是两种不同的类型,但是运行时只加载一个ArrayList到JVM中。

4、泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等价于Object。

5、泛型无法使用基本数据类型,需要使用对应的包装类

6、 异常类不能是泛型的

10.2.3、泛型的继承

需要注意的是当父类、接口声明了泛型时,子类在继承父类或实现接口时,需要分不同的情况去讨论:

  1. 父类定义了泛型,则子类也定义成泛型,完全继承父类

    image-20230218154801789 image-20230218154812404
  2. 父类定义泛型,子类也定义成泛型,但是实现父类一部分泛型

    image-20230218155658411 image-20230218155837401
  3. 父类是泛型,子类不是泛型,需要实现父类的泛型

    image-20230218155143638 image-20230218155201454
  4. 父类是泛型,子类不是泛型,直接忽略父类的泛型,会被默认为Object

    image-20230218155355095 image-20230218155413246
10.2.4、菱形语法

在Java 7以前,如果使用带泛型的接口、类定义变量,那么调用构造器创建对象时构造器的后面也必须带泛型。

image-20230218151014700

这样会显得代码有些多余,而从JDK7之后,Java允许在构造器后需要带完整的泛型信息,只需要一对<>即可,Java可以通过前面的泛型推断出泛型信息。这种写法被称为菱形语法,它没有对泛型做任何改变,只是做了一些简化。

image-20230218151039420

而在 JDK9 中对菱形语法又做了进一步的强化,允许我们在创建匿名内部类时使用菱形语法,Java通过上下文来推断泛型的信息。

image-20230218152625463

泛型并不是一个类,它只是做一个参数的类型约束,比如List在系统上不不会被当成一个新类的,在内存中也只占用一块内存,因此在静态方法、静态初始化块或者静态变量的声明和初始化中不允许使用泛型形参。

image-20230218161820652

10.3、通配符

在数组的定义中,子类和父类是可以协变的,比如说 Dog extends Animal,那么Animal[] 与dog[]是兼容的。

image-20230218162417522

但是如果在集合中是无法这样用的,而且泛型也不是一个类,List 和 List也并没有父子类的关系,它们本质上都是List,所以这时候就需要使用通配符来实现。

通配符其实就是<?> 它表示不确定的Java类型。

具体的使用主要有三种:

  1. 无边界的通配符:
  2. 固定上边界通配符
  3. 固定下边界通配符
10.3.1、无边界的

无边界的通配符只有一个 <?>:

image-20230218162858185

这种方式主要作用就是让泛型能够接受未知类型的数据,这种方式可以使用任何类型的List 来调用,其元素类型是Object的。

image-20230218163714763

但是需要注意的是这种写法仅仅表示是各种泛型的父类,不能向其中添加元素。

image-20230218163830678
10.3.2、固定上边界

我们知道泛型是不协变的。比如我定义动物类,其子类有狗和猫:

abstract class Animal{
    abstract void say();
}

class Dog extends Animal{
    @Override
    void say() {
        System.out.println("是一条狗");
    }
}
class Cat extends Animal{
    @Override
    void say() {
        System.out.println("是一只猫");
    }
}

如果我定义一个方法用来遍历 List集合,那么List和List是传递不进去的,即便内部的数据有几成的关系,但是保存它们的容器并没有继承的关系

image-20230218170135176

所以为了解决这种情况,有一种上边界的通配符,写法是:<? extends T>,这样能在泛型的层面上规定它们的容器也有继承关系。其中通配符的上限是Object

image-20230218170457956

这种通配符的写法和无边界的类似,也是不能添加元素的。指定通配符上限的集合,能从集合中取元素(因为取出的元素总是上限的类型)但是不能向集合中添加元素(因为编译器没法确定集合元素实际是哪种子类型)

为什么能取不能存呢?通俗讲就是:List<? extends Dog>,它存放的数据肯定是一个动物,不管穿过来的是List 还是List,它取的都是一个动物,所以是可以取的。但是存放的时候,它不知道是该存猫还是该存狗,因为传递过来可能是List 和List中的任何一个。

image-20230218170742923

10.3.3、固定下边界

在Java中除了指定上边界外,还能指定下边界,写法是:<? super T>。它的作用和上边界是相反的,指定通配符的下限就是为了支持类型型变,这种型变方式被称为逆变。这种情况下编译器只知道集合元素是下限的父类型,但具体是哪种父类型则不确定,所以下边界只能添加元素(因为实际赋值的集合元素总是逆变声明的父类),不能取元素(取元素时只能被当成Object类型处理)。如果想取元素只能当成Object类来处理了

image-20230218172947390

为什么能存不能取,通俗讲:List<? super Dog>,表示可以存放的数据是Dog类及其父类,其父类是Animal,而Animal 有两个子类Dog和Cat,按类的转型来说,Cat至少都是一个Animal,所以他存放的数据可能存在Cat对象,这样在取的时候,它就会出现错误,因为 <? super Dog> 的边界内是不包含Cat的,所以最后只能都当成Object处理了。而存放时候,我们明确存放进行的类型,所以存放数据是不会出现错误的。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

辰 羽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值