JAVA泛型、泛型通配符、综合练习


作用:

是 jdk5 中引入的特性,可以在编译阶段 约束 操作的数据类型,并进行检查。

格式:

<数据类型>
注意泛型只能支持引用数据类型,基本数据类型可转成对应的包装类。


问题:

在没有泛型的时候,集合如何存储数据?
结论:如果我们没有给集合类型,默认数据为 Object 类型。
即可以添加任意类型的数据
如图:
image.png
image.png
等等;


弊端 1:

我们在获取集合内的数据时,无法使用它里面的特有方法。
如,以下情况:
其实这是构成了一种多态结构,而在讲解多态调用成员的特点中说过
调用成员方法,编译看左边,运行看右边
image.png
需要将 Object 强转成 String 才可以调用。
image.png


此时推出了泛型,
可以再添加数据时把类型进行统一。
而且我们在获取数据的时候,也省的强转了,非常方便。
如图:
image.png
好处
总的来说泛型的好处:

  • 统一数据类型。
  • 把运行时期的问题提前到了编译期间,避免了强制类型转换可能出现的异常,因为在编译阶段类型就能确定下来
  • 扩展:
    • 泛型擦除(Type Erasure)是Java中实现泛型的一种方式,泛型类型信息在编译时会被擦除,使得在运行时无法获取泛型的具体类型信息。这可能会导致一些限制和需要额外注意的地方,比如无法直接创建泛型类型的实例。
      • 例如:image.png

泛型细节:

  • 泛型中不能写基本数据类型
  • 指定泛型的具体类型后,传递数据时,可以传入该类类型或者其子类类型
  • 如果不写泛型,类型默认是Object

泛型的应用:

泛型类:在类名后用

使用场景:当一个类中,某个变量的数据类型不确定时,就可以定义带有泛型的类

格式:
image.png
这里的 E 是用来记录数据类型的,可以写成:T、E、K、V 等等
为了方便理解,在编写泛型类时,可以把自己想想成一个大佬,你需要考虑到使用这个类的人 是如何 使用这个类的。
image.png

现在我们就来实际体会下:

/*
泛型类
 */
public class MYArrayList <E> {

    //ArrayList底层是一个Object数组
    Object[]obj=new Object[10];
    //集合长度
    int size;

    //add方法,其中E表示不确定的类型,e变量名
    public boolean add(E e){
        obj[size]=e;//size既表示长度,又表示下一元素存入位置
        size++;//添加后长度++
        return true;
    }

    //在底层是Object类型,在获取时强转为测试类中泛型指定的类型
    public E get(int index){
        return  (E) obj[index];
    }


    @Override
    public String toString(){
        return Arrays.toString(obj);
    }
}

测试类

public static void main(String[] args) {

    //创建对象,指定类型String
    MYArrayList<String> list = new MYArrayList<>();

    list.add("aaa");
    list.add("bbb");
    list.add("ccc");

    System.out.println(list.get(0));
    System.out.println(list);
    //-----------------------,
    //创建对象,指定类型Integer
    MYArrayList<Integer> list1 = new MYArrayList<>();
    list1.add(123);
    list1.add(123);
    list1.add(123);
    System.out.println(list1.get(0));
    System.out.println(list1);


}

控制台:
aaa
[aaa, bbb, ccc, null, null, null, null, null, null, null]
123
[123, 123, 123, null, null, null, null, null, null, null]


泛型方法:修饰符后声明泛型

应用场景:方法形参类型不确定时,有两种情况

  1. 使用类名候命定义的泛型----所有方法都能用
  2. 在方法的修饰符后声明自己的泛型------只有本方法能用

格式:public static
image.png

用一个例子来理解:
//在工具类 ListUtil 类中定义一个静态方法addAll,用来添加多个集合元素。

//工具类
public class ListUtil {
    //私有化构造方法,不让外界创造对象
    private ListUtil() {
    }

//类中定义一个静态方法add A11,用来添加多个集合的元素。
//    参数一:集合
//    参数二:要添加的元素

    public static <E>void addAll1(ArrayList<E> list,E e1,E e2,E e3){
        list.add(e1);
        list.add(e2);
        list.add(e3);
    }
    
/* public static <E>void addAll2(ArrayList<E> list,E...e){
    for (E element:e){
    list.add(element);
}   */

    }

注释部分: E…e 表示可变参数,用 E[] 来存储,只能通过遍历来获取元素,后面再说

测试类:

//创建集合对象,规定泛型为String
ArrayList<String>list1=new ArrayList<>();

ListUtil.addAll1(list1,"a","b","c");
System.out.println(list1);

//创建集合对象,规定泛型String
ArrayList<Integer>list2=new ArrayList<>();

ListUtil.addAll1(list2,1,2,3);
System.out.println(list2);

若没限定,默认 Object 型

泛型接口:在接口名后用

格式:
image.png

重点:如何使用来个带泛型的接口?

  1. 在实现接口的类中给出具体类型

  2. 在实现类中延续泛型,创建实现类的对象时再确定类型

  3. 在实现接口的类中给出具体类型:

这是一个泛型接口
image.png
现在我们创建一个类来实现接口,并直接给出具体类型
image.png
此时实现类中的 add 方法形参直接确定类型为 String
image.png
所以在测试类中创建实现类的对象后 只能添加 String类型
image.png


  1. 在实现类中延续泛型,创建实现类的对象时再确定类型

现在我们创建一个类来实现 List 接口,但延续泛型
image.png
此时实现类中的 add 方法形参仍是泛型
image.png
创建实现类的对象并确定类型
image.png


泛型的通配符:

严格的泛型类型系统使用起来并不那么令人愉快,
“解决方案”:通配符类型。

  • <?>表示 和 一样也表示不确定的类型,但是?不需要提前定义,而 E 需要提前定义

image.png
image.png
image.png

  • ?extends E:表示可以传递E或者E所有的子类类型
  • ?super E:表示可以传递E或者E所有的父类类型

如果类型不确定,但是能知道以后只能传递某个继承体系中的,就可以泛型的通配符,关键点:可以限定类型的范围。

泛型的继承问题

  • 泛型不具备继承性,但是数据具备继承性

1. 如:
方法 KeepPet3, 形参为 Animal 的集合:image.png
传递一个同是 Animal 类型的集合,传递没问题,
image.png
image.png
这时若换成传递子类 Cat 的集合,会报错
image.png
image.png
所以说泛型不具备继承性

2.但是数据具备继承性image.png

综合练习:

Snipaste_2024-01-25_15-27-20.png

Animal:

public abstract class Animal {
    private String name;
    private int age;

   //构造方法+set+get


    //定义一个抽象方法
    public abstract void eat();

Cat:

public abstract  class Cat extends Animal{

//    调用构造方法
    public Cat(){};
    public  Cat (String name,int age){
        super(name,age);
    }
}

Dog:

public abstract class Dog extends Animal{

    //调用构造方法
    public  Dog(){}
    public Dog(String name, int age){
        super (name ,age);
    }

}

哈士奇:

public class Huskies extends Dog {
    //调用构造
    public Huskies(){};
    public Huskies(String name,int age){
        super(name,age);
    }

    @Override
    public void eat() {
        System.out.println("一只叫做"+getName()+"的,"+getAge()+"岁的哈士奇,正在吃骨头,边吃边拆家");
    }
}

泰迪:

public class Teddy extends Dog{

    //调用构造方法
    public  Teddy(){}
    public Teddy(String name, int age){
        super (name ,age);
    }

    @Override
    public void eat() {
        System.out.println("一只叫做"+getName()+"的,"+getAge()+"岁的泰迪,正在吃骨头,边吃边蹭");
    }
}

波斯猫:

public class PersianCat  extends Cat{
    调用构造方法
public PersianCat(){};
public PersianCat(String name,int age){
super(name,age);
}

    @Override
    public void eat() {
        System.out.println("一只叫做"+getName()+"的,"+getAge()+"岁的波斯猫,正在吃小饼干");
    }


}

狸花猫:

public class TanukiCat extends Cat{

    //调用构造
    public TanukiCat(){};
    public TanukiCat(String name,int age){
        super(name,age);
    }
    @Override
    public void eat() {
        System.out.println("一只叫做"+getName()+"的,"+getAge()+"岁的狸花猫,正在吃鱼");
    }
}

Test:

public class Test {
    public static void main(String[] args) {
        //动物
        ArrayList<Animal> list1 = new ArrayList<>();

        //猫
        ArrayList<Cat> list2 = new ArrayList<>();
        list2.add(new PersianCat("小花", 3));
        list2.add(new TanukiCat("小喵", 2));
        //狗
        ArrayList<Dog> list3 = new ArrayList<>();
        list3.add(new Teddy("小黑", 2));
        list3.add(new Huskies("小汪", 3));


        KeepPet1(list2);//猫
        KeepPet2(list3);//狗
        KeepPet3(list1);//动物
    }

    //要求1该方法能养所有品种的猫,但是不能养狗
    public static void KeepPet1(ArrayList<? extends Cat> list) {
        list.forEach(cat -> cat.eat());
    }


    //要求2:该方法能养所有品种的狗,但是不能养猫
    public static void KeepPet2(ArrayList<? extends Dog> list) {
        Iterator it=list.listIterator();
        while(it.hasNext()){
            Dog dog=(Dog)it.next();
            dog.eat();
        }
    }
    //要求3:该方法能养所有的动物,但是不能传递其他类型
    public static void KeepPet3(ArrayList<Animal> list) {
        for (Animal a : list) {
            a.eat();
        }
    }
}

控制台:
一只叫做小花的,3岁的波斯猫,正在吃小饼干
一只叫做小喵的,2岁的狸花猫,正在吃鱼
一只叫做小黑的,2岁的泰迪,正在吃骨头,边吃边蹭
一只叫做小汪的,3岁的哈士奇,正在吃骨头,边吃边拆家

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

「JavaSE」抽象类接口3

&#x1f387;个人主页&#xff1a;Ice_Sugar_7 &#x1f387;所属专栏&#xff1a;快来卷Java啦 &#x1f387;欢迎点赞收藏加关注哦&#xff01; 抽象类&接口3 &#x1f349;Clonable 接口和深拷贝&#x1f34c;浅拷贝和深拷贝 &#x1f349;Object类&#x1f349;抽象类…

无法获得dpkg前端锁、Linux之E: 无法锁定管理目录(/var/lib/dpkg/),是否有其他进程正占用它?(解决方法)

无法获得dpkg前端锁的解决方法 sudo rm /var/lib/dpkg/lock sudo rm /var/lib/dpkg/lock-frontend sudo rm /var/cache/apt/archives/lock 输入以上三个命令即可解除占用。解除后&#xff0c;继续运行apt命令&#xff0c;已经顺利运行了。解除前端锁后&#xff0c;Linux之E: 无…

.net访问oracle数据库性能问题

问题&#xff1a; 生产环境相同的inser语句在别的非.NET程序相应明显快于.NET程序&#xff0c;执行时间相差比较大&#xff0c;影响正常业务运行&#xff0c;测试环境反而正常。 问题详细诊断过程 问题初步判断诊断过程&#xff1a; 查询插入慢的sql_id 检查对应的执行计划…

20240122面试练习题10

1. Redis为什么执行这么快&#xff1f; 二、Redis为什么这么快&#xff1f; 1、完全基于内存&#xff0c;数据存在内存中&#xff0c;绝大部分请求是纯粹的内存操作&#xff0c;非常快速&#xff0c;跟传统的磁盘文件数据存储相比&#xff0c;避免了通过磁盘IO读取到内存这部分…

配置接口策略路由案例

知识改变命运&#xff0c;技术就是要分享&#xff0c;有问题随时联系&#xff0c;免费答疑&#xff0c;欢迎联系 厦门微思网络​​​​​​ https://www.xmws.cn 华为认证\华为HCIA-Datacom\华为HCIP-Datacom\华为HCIE-Datacom Linux\RHCE\RHCE 9.0\RHCA\ Oracle OCP\CKA\K8S\…

如何使用Jellyfin+cpolar搭建私人影音平台实现无公网ip远程访问

文章目录 1. 前言2. Jellyfin服务网站搭建2.1. Jellyfin下载和安装2.2. Jellyfin网页测试 3.本地网页发布3.1 cpolar的安装和注册3.2 Cpolar云端设置3.3 Cpolar本地设置 4.公网访问测试5. 结语 1. 前言 随着移动智能设备的普及&#xff0c;各种各样的使用需求也被开发出来&…

jQuery实现选择方法和保护信息方法

最近呢&#xff01;一直在学习jQuery语法&#xff0c;也没时间发布文章&#xff0c;现在学的差不多了&#xff0c;先跟大家分享下学习感受吧&#xff01;JavaScript学过后&#xff0c;再学习jQuery语法&#xff0c;应该是简单的&#xff0c;但我总是容易把它们搞混&#xff0c;…

Alzet渗透泵工作原理,你清楚么?

由于Alzet渗透泵独特的释放原理&#xff0c;使得许多化学试剂、药剂或者其他物质&#xff0c;可以通过Alzet渗透泵应用到科研试验中。一个小小的胶囊就可以完成持续的动物给药实验&#xff0c;对于科研实验者来说就是一个福音。 那你了解Alzet渗透泵么?让我们一起来简单了解一…

Android HIDL概述与绑定模式的实现

一、前言 Android O(8.0) 版本之后&#xff0c;底层实现有了比较大的变化&#xff0c;最显著的一个方面就是 HIDL 机制的全面实施。本文对于理解系统源码中 Gnss、Usb、Camera 等模块的工作原理有极大帮助。 二、HIDL 设计目的 在 Android O(8.0) 之前系统的升级牵扯多方协作…

循环购:电商新时代的消费革命

随着电商市场的竞争加剧&#xff0c;消费者需求日益多样化&#xff0c;电商企业需要不断创新以满足市场需求。循环购模式作为一种新兴的消费模式&#xff0c;正逐渐受到消费者的青睐&#xff0c;成为电商行业的新热点。本文将深入剖析循环购模式的魅力&#xff0c;探索其在电商…

chromedriver下载地址与安装方法

用到例如selenium时&#xff0c;需要使用谷歌浏览器并配合chromedriver进行模拟浏览等 分享chromedriver的实时更新的下载网址&#xff1a; https://registry.npmmirror.com/binary.html?pathchromedriver/ 。。。 需要找到自己电脑谷歌浏览器版本所对应的chromedriver版本 …

CQ 社区版 2.8.0 | 支持TiDB、StarRocks,新增列过滤算法、导出模式设置等

Hello&#xff0c;CloudQuery 社区版 2.8.0 已发布&#xff0c;本文将带大家详细解析本次更新的功能~&#xff08;完整的讲解视频可点击 &#x1f449;&#x1f3fb; CloudQuery 社区版2.8.0 功能讲解演示 本期亮点更新 新增支持数据源 TiDB、StarRocks数据保护新增列过滤脱敏…

HNSW算法

From&#xff1a; HNSW算法(nsmlib/hnswlib)-CSDN博客HNSW算法的基本原理及使用 - 知乎 HNSW是一种广泛使用的ANN图索引结构&#xff0c;包括DiskANN、DF-GAS、SmartSSD等。本文档主要总结HNSW的结构与工作流程&#xff0c;便于后期研究其工作流程在迁移到CSD中存在的I/O问题…

计算CNN卷积层和全连接层的参数量

计算CNN卷积层和全连接层的参数量 先前阅读 CNN ExplainerA Comprehensive Guide to Convolutional Neural Networks — the ELI5 way 本文主旨意在搞明白2个问题&#xff1a; 第一个问题 一个卷积操作&#xff0c;他的参数&#xff0c;也就是我们要训练的参数&#xff0c;也…

50. Pow(x, n)

分治算法&#xff1a; 从右往左开始递归计算&#xff0c;假设yx^(n/2)&#xff0c;那么当n为偶数时&#xff0c;x^ny*y&#xff0c;当n为奇数时&#xff0c;x^ny*y*x。 另外&#xff0c;注意n有可能是负数。 class Solution {public double myPow(double x, int n) {int N n…

Kettle-Docker部署+Sqlserver数据同步Mysql+Start定时任务

一. 背景介绍 1. ETL是什么 ETL&#xff08;Extract-Transform-Load&#xff09;&#xff0c;即数据抽取、转换、装载的过程。它是一种思想&#xff0c;主要是说&#xff0c;从不同的数据源获取数据&#xff0c;并通过对数据进行处理&#xff08;格式&#xff0c;协议等转换&a…

ChatGPT 全域调教高手:成为人工智能交流专家

随着人工智能的快速发展&#xff0c;ChatGPT作为一种强大的文本生成模型&#xff0c;在各行各业中越来越受到重视和应用。想要利用ChatGPT实现更加智能、自然的交流&#xff0c;成为 ChatGPT 全域调教高手吗&#xff1f;本文将为您介绍如何通过优化ChatGPT的训练方法&#xff0…

全新5IUX极简搜索主页源码 /自定义浏览器主页

源码介绍&#xff1a; 全新5IUX极简搜索主页源码&#xff0c;专为自定义浏览器主页而设计。厌倦了各种导航首页上满屏幕的广告和资讯&#xff0c;可以自己尝试编写一个个性化的主页。这款源码并非镜像或代理&#xff0c;而是作为浏览器主页使用&#xff0c;同时支持自适应屏幕…

springboot快速写接口

1. 建proj形式 name会变成文件夹的名字&#xff0c;相当于你的项目名称 基础包 2. 基础依赖 3. 配置数据库 这里要打开mysql&#xff0c;并且创建数据库 方法&#xff1a; 安装好数据库&#xff0c;改好账号密码用navicat来建表和账号配置properties.yml文件即可 4.用res…

基于SpringBoot的玩具租赁系统

文章目录 项目介绍主要功能截图&#xff1a;部分代码展示设计总结项目获取方式 &#x1f345; 作者主页&#xff1a;超级无敌暴龙战士塔塔开 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、 简历模板、学习资料、面试题库【关注我&#xff0c;都给你】 &…