Java基础-----集合类(二)

请添加图片描述

文章目录

  • 1. 泛型简介
  • 2. 使用泛型的好处
  • 3.使用泛型
    • 3.1 泛型类
    • 3.2 泛型接口
    • 3.3 泛型方法
  • 4 泛型的通配符
    • 4.1 <?>:无边界的通配符
    • 4.2 <? extends E>:固定上边界的通配符
    • 4.3 <? super E>:固定下边界的通配符
  • 5.总结

今天主要学习泛型

1. 泛型简介

在我们自定义的集合类中,底层是Object类型的数组,在设计和声明时,不能确定这个容器里到底要存储什么类型的数据。从JDK5版本之后,引入一个新的特性----泛型,提供了编译时的类型安全检测机制,该机制允许程序员在编译时检测到非法的数据类型。

泛型允许在定义类、接口时通过一个标识来表示其中某个属性的类型或者某个方法的返回值及参数类型。泛型的本质是参数化类型,给类型指定一个参数,然后在使用时再指定参数具体的值,这样类型可以在使用时决定了。

这种参数类型可以用在类、接口、方法中,分别称为泛型类、泛型接口和泛型方法。

2. 使用泛型的好处

  • 保证了类型的安全性

如果没有使用泛型,在集合中存储和读取数据,都是Object类型。如果要将数据读取成特定类型,需要对每一个对象进行强制转换,如果存储的对象数据类型错误,在转换时会报异常。

  • 消除强制转换

使代码可读性更强,减少出错机会

  • 避免了不必要的拆箱封箱操作
  • 提高了代码的重用性

3.使用泛型

3.1 泛型类

把泛型定义在类上,格式:

public  class 类名<泛型>

例如:

public class MyArray<E>{}
  • 泛型类型必须是引用类型,基本数据类型不可以

  • 定义泛型类,在类后添加一对尖括号,在尖括号中填类型参数,参数可以有多个,多个参数用逗号分隔。

规范泛型使用字母的表示信息:

  • T:Type(java类)

  • E:Element(在集合中使用,指集合中存放的元素)

  • K:Key(键)

  • V:Value(值)

  • N:Number(数值类型)

  • ?:表示不确定的java类型

public class GenericClass<T> {
    private T value;

    public GenericClass() {
    }

    public GenericClass(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }
}
public class TestGenericClass {
    public static void main(String[] args) {
        //钻石符号
        GenericClass<Integer> c=new GenericClass<>(123);
        Integer x = c.getValue();
        System.out.println(x);//123
        c.setValue(456);
        System.out.println(c.getValue());//456

        GenericClass<String> c1=new GenericClass<>("这是一个字符串");
        String s = c1.getValue();
        System.out.println(s);//这是一个字符串
    }
}

3.2 泛型接口

定义方式:

public  interface  接口名<泛型>
public interface GenericInterface<T> {
    void showValue(T value);
}
class imple implements GenericInterface<String >{

    @Override
    public void showValue(String value) {
        System.out.println(value);
    }
}
class imple1<T> implements GenericInterface<T>{

    @Override
    public void showValue(T value) {
        System.out.println(value);
    }
}
public class TestGenericInterface {
    public static void main(String[] args) {
        GenericInterface<String> c=new imple();
        c.showValue("hello");

        GenericInterface<Integer> x=new imple1<>();
        x.showValue(123);

        GenericInterface<String > y=new imple1<>();
        y.showValue("hello world");
    }
}

3.3 泛型方法

在调用方法时指明泛型的具体类型(参数和返回值)

定义格式:

1.没有返回值类型,有参数

public < T> void 方法名(T  t){}

2.有返回值类型,可以有参数,也可以没有参数

public < T>  T  方法名([T  t]){}
public class GenericMethod {
    /**
     * 没有返回值类型,有参数
     *public < T> void 方法名(T  t){}
     */
    public <T> void method1(T t){
        System.out.println(t.toString());
    }
    /**
     * 有返回值类型,可以有参数,也可以没有参数
     *public < T>  T  方法名([T  t]){}
     */
    public <T> T method2(T t){
        System.out.println(t.getClass().getName());
        return t;
    }

}
class Student{
    private String name;
    private String gender;

  //此处省略相关的Getter()和Setter()方法

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                '}';
    }
}
class Teacher{
    private String name;
    private Integer age;
    
//此处省略相关的Getter()和Setter()方法

    @Override
    public String toString() {
        return "Teacher{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class TestGenericMethod {
    public static void main(String[] args) {
        Student student=new Student("李白","男");
        GenericMethod c=new GenericMethod();
        c.method1(student);

        Teacher teacher=new Teacher("李清照",25);
        c.method1(teacher);

        System.out.println("------------------------");
        c.method2(student);
        c.method2(teacher);

    }
}

4 泛型的通配符

用于解决泛型之间引用传递问题的特殊用法,主要分成三种情况:

4.1 <?>:无边界的通配符

主要作用是让泛型能够接受未知类型的数据。在没有赋值前,表示可以接受任何的数据类型,赋值之后不能往里面随便添加元素,因为不知道集合的数据类型,只能做读取操作,并且读到元素当成Object实例操作,但是可以去执行remove移除和clear清空操作。

使用情况:

  • 用于编写可使用Object类中提供的功能方法时
  • 代码使用不依赖于类型参数的泛型类中的方法时
public class TestGenericWildCard {
    public static void main(String[] args) {
        MyArray<Integer> myArray=new MyArray<>();
        myArray.add(123);
        myArray.add(456);
        myArray.add(789);
        test(myArray);
    }
    //不知道集合是哪种类型,只能进行读取操作,读取到的元素都当成Object对象来处理
    public static void test(MyArray<?> array){
        //在没有赋值前,表示可以接受任何的数据类型,赋值之后不能往里面随便添加元素
        //array.add(100);//报错

        /*//可以执行删除操作
        array.remove(0);
        //不知道集合是哪种类型,只能进行读取操作,读取到的元素都当成Object对象来处理
        for(int i=0;i<array.size();i++){
            System.out.println(array.get(i));
        }*/

        //可以做清除操作
        array.clear();
        System.out.println(array.size());//0

    }
}

4.2 <? extends E>:固定上边界的通配符

协变:在使用父类类型场景的地方可以改用子类类型(也就是说,父类出现的地方子类一定可以出现)

逆变:在使用子类类型场景的地方可以改用父类类型

不变:不能做到以上两点

数组是可以协变的。泛型不是协变的。这种设计降低了程序的灵活性,为了解决这个问题,设计出固定上边界的通配符。能够接受指定类及其子类类型的数据。

虽然用的是extends关键字,但不限于继承了父类的子类,也可以使用接口的实现类

使用上限通配符只能从集合中获取值,而不能将值放入集合中。

public class TestArray {
    public static void main(String[] args) {
        //数组是可以协变的
        //在X数组中,0的位置存储的是Y,1的位置存储的是Z,
        //在转换的过程中会报错,会产生ArrayStoreException异常
        X[] x=new X[2];
        x[0]=new Y();
        x[1]=new Z();//抛异常:ArrayStoreException
        //为了解决这个问题,就提出了泛型的概念     
    }
}
class X{}
class Y extends X{}
class Z extends X{}
public class TestArray {
    public static void main(String[] args) {
        //泛型不是协变的
        //虽然Y是继承自X的,但是把Y放进X中时会报错,所以泛型不是协变的
        MyArray<Y> array=new MyArray<>();
        test(array);//报错  
    }
    public  static void test(MyArray<X> array){

    }
}
class X{}
class Y extends X{}
class Z extends X{}

报错信息:
在这里插入图片描述

上面的程序使用固定上边界的通配符<? extends E>就可以了

public class TestArray {
    public static void main(String[] args) {
 //使用固定上边界的通配符<? extends E>就可以了      
        MyArray<Y> array=new MyArray<>();
        test(array);
        MyArray<Z> array1=new MyArray<>();
        test(array1);
    }

    public  static void test(MyArray<? extends X> array){
    }
}

class X{}
class Y extends X{}
class Z extends X{}

虽然用的是extends关键字,但不限于继承了父类的子类,也可以使用接口的实现类。

public class TestArray {
    public static void main(String[] args) {
//虽然用的是extends关键字,但不限于继承了父类的子类,也可以使用接口的实现类
        MyArray<MyImpl1> inter=new MyArray<>();
        test(inter);

    }
    public static void test(MyArray<? extends MyInter1> inter){

    }

}
interface MyInter1{}
class MyImpl1 implements MyInter1{}
class MyImpl2 implements MyInter1{}
public class TestArray {
    public static void main(String[] args) {

//虽然用的是extends关键字,但不限于继承了父类的子类,也可以使用接口的实现类
        MyArray<MyImpl1> inter=new MyArray<>();
        test(inter);
    }

    public static void test(MyArray<? extends MyInter1> inter){
        inter.add(new MyImpl1());//报错  原因是:使用上限通配符只能从集合中获取值,而不能将值放入集合中
    }
}
interface MyInter1{}
class MyImpl1 implements MyInter1{}
class MyImpl2 implements MyInter1{}

程序报错信息:此错误出现的原因是:使用上限通配符只能从集合中获取值,而不能将值放入集合中。如下图所示:
在这里插入图片描述
做读取操作是可以的:

public class TestArray {
    public static void main(String[] args) {
//虽然用的是extends关键字,但不限于继承了父类的子类,也可以使用接口的实现类
        MyArray<MyImpl1> inter=new MyArray<>();
        inter.add(new MyImpl1());
        inter.add(new MyImpl1());
        inter.add(new MyImpl1());
        inter.add(new MyImpl1());
        test(inter);

    }

    public static void test(MyArray<? extends MyInter1> inter){
        //inter.add(new MyImpl1());
        //做读取操作是可以的
        for (int i=0;i<inter.size();i++){
            System.out.println(inter.get(i));
        }
    }
}
interface MyInter1{}
class MyImpl1 implements MyInter1{}
class MyImpl2 implements MyInter1{}

4.3 <? super E>:固定下边界的通配符

接受指定类及其父类类型(或接口)的数据

可以读取到集合的数据,按照Object类型处理

可以向方法中添加元素,添加的只能是指定类或其子类类型的对象,不能添加父类或接口类型的对象

public class TestArray1 {
    public static void main(String[] args) {
        MyArray<MyInter> array=new MyArray<>();
        array.add(new MyImple2());
        array.add(new MyImple2());
        test1(array);
    }

    public static void test1(MyArray<? super MyImple1> array){   
       // MyImple1 object= (MyImple1) array.get(0);//报错java.lang.ClassCastException类转换异常
       //可以读取到集合的数据,按照Object类型处理
        Object object=array.get(0);
        System.out.println(object);

    }
}
interface MyInter{}
class MyImple1 implements MyInter{}
class MyImple2 implements MyInter{}
//可以向方法中添加元素,添加的只能是指定类或其子类类型的对象,不能添加父类或接口类型的对象
public class TestArray1 {
    public static void main(String[] args) {
        MyArray<MyInter> array=new MyArray<>();
        array.add(new MyImple2());
        array.add(new MyImple2());
        test1(array);
    }

    public static void test1(MyArray<? super MyImple1> array){            
        //消费是可以的
        //添加元素时,不能加指定类的父类对象
        //添加的只能是指定类或其子类类型的对象
        array.add(new MyImple1());//添加当前指定类对象
        array.add(new SubImpl());//添加当期那指定类的子类对象
        //array.add(new MyImple2());//报错
        //array.add(new MyInter());//报错
    }
}
interface MyInter{}
class MyImple1 implements MyInter{}
class MyImple2 implements MyInter{}
class SubImpl extends MyImple1{}

5.总结

  • 1.如果要从集合中获取值,使用上限通配符

  • 2.如果要向集合中放入数据值,使用下限通配符

  • 3.可以为通配符指定上限,也可以指定下限,但不能同时指定两者

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

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

相关文章

Invalid bound statement (not found)异常解决方案归纳

一、包名映射不对&#xff08;新建多级mapper文件夹引起的&#xff0c;解决方案在下面链接有详细解释&#xff09; Invalid bound statement (not found)异常解决_invalid bound statement (not found): com.ruoyi.map-CSDN博客 二、mapper.xml中的namespace和实际的mapper文…

软考证书学下来,对找工作有哪些帮助?

“软考&#xff1f;真的别考”。 “考了半天&#xff0c;到过期了还能没用上&#xff0c;hr根本不看”。 我在豆瓣上看着网友们的评论&#xff0c;心中五味杂陈。 每年有超过100万人参加的考试&#xff0c;却被人吐槽说一无是处&#xff1f; 这种国家级认证的考试&#xff0…

小白也能看得懂的Jmeter性能测试中服务端资源监控技术

操作步骤&#xff1a; 1、安装插件管理器 插件管理器的作用&#xff1a;可以提供扩展插件的在线安装升级和卸载。因为我们需要在线安装监控插件&#xff0c;首先我们就要先安装插件管理器。 插件管理器的下载地址&#xff1a;https://jmeter-plugins.org/install/Install/ 如…

【论文笔记】An Extractive-and-Abstractive Framework for Source Code Summarization

An Extractive-and-Abstractive Framework for Source Code Summarization 1. Introduction2. Model2.1 Overview2.2 Training of EACS2.2.1 Part i : Training of Extractor2.2.2 Part ii : Training of Abstracter 3. Evaluation 1. Introduction 代码摘要可以细分为抽取式代…

LeetCode2.AddTwoNumbers两数相加(Java可运行,带测试用例)

一、题目 给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的&#xff0c;并且每个节点只能存储 一位 数字。 请你将两个数相加&#xff0c;并以相同形式返回一个表示和的链表。 你可以假设除了数字 0 之外&#xff0c;这两个数都…

十大性能测试工具

这篇关于“性能测试工具”的文章将按以下顺序让您了解不同的软件测试工具&#xff1a; 什么是性能测试&#xff1f;为什么我们需要性能测试&#xff1f;性能测试的优势性能测试的类型十大性能测试工具 什么是性能测试&#xff1f; 性能测试是一种软件测试&#xff0c;可确保…

OpenFeign相关面试题及答案(2024)

1、什么是OpenFeign&#xff0c;它如何简化远程服务调用&#xff1f; OpenFeign是一个声明式的Web服务客户端&#xff0c;它使得编写HTTP客户端变得更加容易。它属于Spring Cloud Netflix项目的一部分&#xff0c;可以与Spring Boot应用轻松集成。通过使用OpenFeign&#xff0…

Python数据挖掘与机器学习实践技术应用

近年来&#xff0c;Python编程语言受到越来越多科研人员的喜爱&#xff0c;在多个编程语言排行榜中持续夺冠。同时&#xff0c;伴随着深度学习的快速发展&#xff0c;人工智能技术在各个领域中的应用越来越广泛。机器学习是人工智能的基础&#xff0c;因此&#xff0c;掌握常用…

金融追梦者,向着春天出发——社科院与美国杜兰大学金融管理硕士

随着时代的进步和社会的变迁&#xff0c;教育已经不再是单纯的学生时代的事情&#xff0c;而是贯穿人的一生。特别是在金融行业&#xff0c;由于其变幻莫测的特性&#xff0c;在职继续攻读硕士学位的人越来越多。他们希望通过进一步的学习和研究&#xff0c;提升自己的专业素养…

网络安全行业必考证书(NISP/CISP)

&#x1f50d; 网络安全行业需要考哪些证书&#xff1f; &#x1f4dd; 在网络安全行业&#xff0c;证书是非常重要的敲门砖。拥有一定的证书可以证明你具备相关的知识和技能&#xff0c;能够胜任相关的工作&#xff0c;对于企业投标也能更方便些。 &#x1f4dd;当之无愧的必然…

【深度学习-基础学习】Transformer 笔记

本篇文章学习总结 李宏毅 2021 Spring 课程中关于 Transformer 相关的内容。课程链接以及PPT&#xff1a;李宏毅Spring2021ML这篇Blog需要Self-Attention为前置知识。 Transfomer 简介 Transfomer 架构主要是用来解决 Seq2Seq 问题的&#xff0c;也就是 Sequence to Sequence…

IO进程线程 day4 文件IO与目录操作

1.使用标准IO完成两个文件拷贝 #include <head.h> int main(int argc, const char *argv[]) {//判断输入是否合法if(argc>3){printf("输入不合法\n");return -1;}//定义两个文件指针&#xff0c;用于读写FILE *fp1NULL;FILE *fp2NULL;if((fp1fopen(argv[1]…

ASP.Net实现海鲜添加(三层架构,异常处理)

演示功能&#xff1a; 点击启动生成页面 点击添加跳转新界面 此处设置文本框多行 点击Button添加 步骤&#xff1a; 1、建文件 下图是三层架构列表&#xff0c;Models里面有模拟数据库中列的类&#xff0c;DAL中有DBHelper和service,BLL中有BllManager文件用于ui界面直接调用…

FreeRTOS——信号量知识点总结及二值信号量实战

1信号量概念 1&#xff09;信号量的计数值都有限制&#xff1a;限定最大值。 如果最大值被限定为1&#xff0c;那么它就是二值信号量&#xff1b; 如果最大值不是1&#xff0c;它就是计数型信号量。 2&#xff09;当计数值大于0&#xff0c;代表有信号量资源 当释放信号量&…

模型融合之模型堆叠

一、理论 模型堆叠&#xff08;Model Stacking&#xff09;是一种集成学习的方法&#xff0c;其本质是将多个基学习器&#xff08;Individual Learner&#xff09;的预测结果作为新的特征&#xff0c;再训练一个元学习器&#xff08;Meta Learner&#xff09;来进行最终的预测。…

探索 Vue 实例方法的魅力:提升 Vue 开发技能(上)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

Green Sock | GSAP 动画库

1.什么是“GSAP”&#xff1f; GreenSock Animation Platform&#xff08;GSAP&#xff09; 是一个业界知名的动画工具套件&#xff0c;在超过1100万个网站上使用&#xff0c;其中包括大量获奖网站&#xff01; 您可以使用GSAP在任何框架中制作几乎任何JavaScript可以触及的动…

迅腾文化传播:触动每个移动消费者心灵的品牌故事缔造者

迅腾文化传播&#xff1a;触动每个移动消费者心灵的品牌故事缔造者 在这个高速发展的移动互联网时代&#xff0c;信息如同浩渺星海中的流星&#xff0c;瞬息万变。每个人的手机、平板、智能手表等移动设备&#xff0c;都成为了他们与世界连接的窗口。品牌&#xff0c;作为这个…

谷歌推出创新SynCLR技术:借助AI生成的数据实现高效图像建模,开启自我训练新纪元!

谷歌推出了一种创新性的合成图像框架&#xff0c;这一框架独特之处在于它完全不依赖真实数据。这个框架首先从合成的图像标题开始&#xff0c;然后基于这些标题生成相应的图像。接下来&#xff0c;通过对比学习的技术进行深度学习&#xff0c;从而训练出能够精准识别和理解这些…

STM32 学习(三)OLED 调试工具

目录 一、简介 二、使用方法 2.1 接线图 2.2 配置引脚 2.3 编写代码 三、Keil 工具调试 一、简介 在进行单片机开发时&#xff0c;有很多调试方法&#xff0c;如下图&#xff1a; 其中 OLED 就是一种比较好用的调试工具&#xff1a; OLED 硬件电路如下&#xff0c…