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);//123c.setValue(456);System.out.println(c.getValue());//456GenericClass<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 >{@Overridepublic void showValue(String value) {System.out.println(value);}
}
class imple1<T> implements GenericInterface<T>{@Overridepublic 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()方法@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", gender='" + gender + '\'' +'}';}
}
class Teacher{private String name;private Integer age;//此处省略相关的Getter()和Setter()方法@Overridepublic 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.可以为通配符指定上限,也可以指定下限,但不能同时指定两者

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

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

相关文章

【React】class组件生命周期函数的梳理和总结(第一篇)

1. 前言 本篇梳理和总结一下React的生命周期函数&#xff0c;方便使用class组件的同学查阅&#xff0c;先上生命周期图谱。 2. 生命周期函数 生命周期函数说明constructor(props) 功能&#xff1a;如果不需要初始化state或不进行方法绑定&#xff0c;class组件可以不用实现构造…

【华为机试】2023年真题B卷(python)-考古问题

一、题目 题目描述&#xff1a; 考古问题&#xff0c;假设以前的石碑被打碎成了很多块&#xff0c;每块上面都有一个或若干个字符&#xff0c;请你写个程序来把之前石碑上文字可能的组合全部写出来&#xff0c;按升序进行排列。 二、输入输出 三、示例 示例1: 输入输出示例仅供…

BLE协议—协议栈基础

BLE协议—协议栈基础 BLE协议栈基础通用访问配置文件层&#xff08;Generic Access Profile&#xff0c;GAP&#xff09;GAP角色设备配置模式和规程安全模式广播和扫描 BLE协议栈基础 蓝牙BLE协议栈包含三部分&#xff1a;主机、主机接口层和控制器。 主机&#xff1a;逻辑链路…

resetlogs失败故障恢复-ORA-01555---惜分飞

客户数据库resetlogs报错 Tue Dec 19 15:21:23 2023 ALTER DATABASE MOUNT Successful mount of redo thread 1, with mount id 1683789043 Database mounted in Exclusive Mode Lost write protection disabled Completed: ALTER DATABASE MOUNT Tue Dec 19 15:22:01 2023…

视频云存储/视频智能分析平台EasyCVR在麒麟系统中无法启动该如何解决?

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…

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

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

OpenFeign

OpenFeign 一、基本使用 1、引入依赖 <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId><groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-start…

lwip发送组播数据问题

1、今天测试组播包发现&#xff0c;组播数据只能在默认网卡发送成功&#xff0c;多次交叉测试依然这样&#xff0c;所以和网卡的配置无关 &#xff08;我的是双网卡&#xff09; 2、最后搜源码看&#xff0c;才发现有一段代码如下&#xff1a; struct netif * ip4_route(cons…

Vivado JESD204B与AD9162建立通信实战总结

一、FPGA与AD9162的JESD204B接口 FPGA作为JESD204B接口的发送端&#xff0c;AD9162作为JESD204B接口的接收端。FPGA和AD9162的device clk、SYSREF由同源时钟芯片产生。其中&#xff0c;FPGA和AD9162的divice clk时钟不同&#xff0c;并且FPGA的decive clk等同于JESD204B IP的co…

Ubuntu 22.04/20.04 安装 SSH

OpenSSH 是安全远程通信的重要工具&#xff0c;提供了一种安全的方式来访问和管理服务器。对于那些计划在 Ubuntu 22.04 Jammy Jellyfish 或其较旧的稳定版本的 Ubuntu 20.04 Focal Fossa 上安装 SSH 并启用它的人来说&#xff0c;了解其功能和优势至关重要。 OpenSSH的主要特…

x-cmd pkg | zellij - 比 tmux 更容易上手的终端多路复用器

简介 zellij 是一个面向开发、运营以及任何热爱终端的人的终端多路复用器 &#xff08;Terminal Multiplexers&#xff09;&#xff0c;类似于 tmux 和 screen&#xff0c;内置许多功能&#xff0c;允许用户扩展并创建自己的个性化环境。 zellij 的设计理念是不牺牲简单性来换…

Excel·VBA二维数组组合函数的应用实例之概率计算

看到一个视频《李永乐老师的抖音 - 骰子概率问题》&#xff0c;计算投出6个骰子恰好出现1、2、3、4、5、6这6个点数的概率 李永乐老师的计算方法是&#xff0c;第1个概率为1即6/6&#xff0c;第2个不与之前相同的概率为5/6&#xff0c;第3个同理概率为4/6&#xff0c;因此该问…