java中对泛型的理解

news2024/10/6 12:32:13

那么什么是泛型

泛型:是一种把明确类型的工作推迟到创建对象或者调用方法的时候才去明确的特殊的类型。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,而这种参数类型可以用在类、方法和接口中,分别被称为泛型类、泛型方法、泛型接口。

注意:一般在创建对象时,将未知的类型确定具体的类型。当没有指定泛型时,默认类型为Object类型。

使用泛型会有哪些好处

  • 避免了类型强转的麻烦。

  • 它提供了编译期的类型安全,确保在泛型类型(通常为泛型集合)上只能使用正确类型的对象,避免了在运行时出现ClassCastException。

如何使用泛型

泛型虽然通常会被大量的使用在集合当中,但是我们也可以完整的学习泛型只是。泛型有三种使用方式,分别为:泛型类、泛型方法、泛型接口。将数据类型作为参数进行传递。

泛型类

泛型类型用于类的定义中,被称为泛型类。通过泛型可以完成对一组类的操作对外开放相同的接口。最典型的就是各种集合框架容器类,如:List、Set、Map。

泛型类的定义格式:
修饰符 class 类名<代表泛型的变量> {  }

怕你不清楚怎么使用,这里我还是做了一个简单的泛型类:
/**
 * @param <T> 这里解释下<T>中的T:
 *           此处的T可以随便写为任意标识,常见的有T、E等形式的参数表示泛型
 *           泛型在定义的时候不具体,使用的时候才变得具体。
 *           在使用的时候确定泛型的具体数据类型。即在创建对象的时候确定泛型。
 */
public class GenericsClassDemo<T> {
 //t这个成员变量的类型为T,T的类型由外部指定
 private T t;
​
 //泛型构造方法形参t的类型也为T,T的类型由外部指定
 public GenericsClassDemo(T t) {
 this.t = t;
    }
​
 //泛型方法getT的返回值类型为T,T的类型由外部指定
 public T getT() {
 return t;
    }
}
泛型在定义的时候不具体,使用的时候才变得具体。在使用的时候确定泛型的具体数据类型。即:在创建对象的时候确定泛型。

例如:Generic<String> genericString = new Generic<String>("helloGenerics");
此时,泛型标识T的类型就是String类型,那我们之前写的类就可以这么认为:
public class GenericsClassDemo<String> {
 private String t;
​
 public GenericsClassDemo(String t) {
 this.t = t;
    }
​
 public String getT() {
 return t;
    }
}
当你的泛型类型想变为Integer类型时,也是很方便的。直接在创建时,T写为Integer类型即可:
Generic<Integer> genericInteger = new Generic<Integer>(666);

注意: 定义的泛型类,就一定要传入泛型类型实参么?

并不是这样,在使用泛型的时候如果传入泛型实参,则会根据传入的泛型实参做相应的限制,此时泛型才会起到本应起到的限制作用。如果不传入泛型类型实参的话,在泛型类中使用泛型的方法或成员变量定义的类型可以为任何的类型。即跟之前的经典案例一样,没有写ArrayList的泛型类型,容易出现类型强转的问题。

泛型方法

定义格式:
修饰符 <代表泛型的变量> 返回值类型 方法名(参数){  }

例如:
/**
 *
 * @param t 传入泛型的参数
 * @param <T> 泛型的类型
 * @return T 返回值为T类型
 * 说明:
 *   1)public 与 返回值中间<T>非常重要,可以理解为声明此方法为泛型方法。
 *   2)只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
 *   3)<T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
 *   4)与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E等形式的参数常用于表示泛型。
 */
 public <T> T genercMethod(T t){
 System.out.println(t.getClass());
 System.out.println(t);
 return t;
    }

调用方法时,确定泛型的类型
public static void main(String[] args) {
 GenericsClassDemo<String> genericString = new GenericsClassDemo("helloGeneric"); //这里的泛型跟下面调用的泛型方法可以不一样。
​
 String str = genericString.genercMethod("hello");//传入的是String类型,返回的也是String类型
 Integer i = genericString.genercMethod(123);//传入的是Integer类型,返回的也是Integer类型
}

这里我们可以看下结果:
class java.lang.String
hello
class java.lang.Integer
123
这里可以看出,泛型方法随着我们的传入参数类型不同,他得到的类型也不同。泛型方法能使方法独立于类而产生变化。

泛型接口

泛型接口与泛型类的定义及使用基本相同。泛型接口常被用在各种类的生产器中。

定义格式
修饰符 interface接口名<代表泛型的变量> {  }

看一下下面的例子,你就知道怎么定义一个泛型接口了:
/**
 * 定义一个泛型接口
 */
public interface GenericsInteface<T> {
 public abstract void add(T t); 
}

使用格式

1、定义类时确定泛型的类型
public class GenericsImp implements GenericsInteface<String> {
 @Override
 public void add(String s) {
 System.out.println("设置了泛型为String类型");
    }
}

2、始终不确定泛型的类型,直到创建对象时,确定泛型的类型
public class GenericsImp<T> implements GenericsInteface<T> {
 @Override
 public void add(T t) {
 System.out.println("没有设置类型");
    }
}

确定泛型:
public class GenericsTest {
 public static void main(String[] args) {
 GenericsImp<Integer> gi = new GenericsImp<>();
 gi.add(66);
    }
}
    • 泛型通配符

当使用泛型类或者接口时,传递的数据中,泛型类型不确定,可以通过通配符<?>表示。但是一旦使用泛型的通配符后,只能使用Object类中的共性方法,集合中元素自身方法无法使用。
    • 通配符基本使用

泛型的通配符:不知道使用什么类型来接收的时候,此时可以使用?,?表示未知通配符。

此时只能接受数据,不能往该集合中存储数据。

举个例子大家理解使用即可

// ?代表可以接收任意类型
// 泛型不存在继承、多态关系,泛型左右两边要一样
//ArrayList<Object> list = new ArrayList<String>();这种是错误的
​
//泛型通配符?:左边写<?> 右边的泛型可以是任意类型
ArrayList<?> list1 = new ArrayList<Object>();
ArrayList<?> list2 = new ArrayList<String>();
ArrayList<?> list3 = new ArrayList<Integer>();
​

注意:泛型不存在继承、多态关系,泛型左右两边要一样,jdk1.7后右边的泛型可以省略

而泛型通配符?,右边的泛型可以是任意类型。

泛型通配符?主要应用在参数传递方面,让我们一起瞧瞧呗:
public static void main(String[] args) {
    ArrayList<Integer> list1 = new ArrayList<Integer>();
    test(list1);
    ArrayList<String> list2 = new ArrayList<String>();
    test(list2);
}
​
public static void test(ArrayList<?> coll){
}

嘿嘿,是不是见识到了通配符的厉害,可以传递不同类似进去方法中了!

    • 通配符高级使用

之前设置泛型的时候,实际上是可以任意设置的,只要是类就可以设置。但是在JAVA的泛型中可以指定一个泛型的上限下限

泛型的上限

格式: 类型名称 <? extends 类 > 对象名称

意义: 只能接收该类型及其子类

泛型的下限

格式: 类型名称 <? super 类 > 对象名称

意义: 只能接收该类型及其父类型

比如:现已知Object类,Animal类,Dog类,Cat类,其中Animal是Dog,Cat的父类

classAnimal{}//父类
​classDogextendsAnimal{}//子类
​classCatextendsAnimal{}//子类
首先我们先看下,泛型的上限<? extends 类 >:
//        ArrayList<? extends Animal> list = new ArrayList<Object>();//报错
 ArrayList<? extends Animal> list2 = new ArrayList<Animal>();
 ArrayList<? extends Animal> list3 = new ArrayList<Dog>();
 ArrayList<? extends Animal> list4 = new ArrayList<Cat>();
可以看出,泛型的上限只能是该类型的类型及其子类。
我们再来看看泛型的下限<? super 类 >:
 ArrayList<? super Animal> list5 = new ArrayList<Object>();
 ArrayList<? super Animal> list6 = new ArrayList<Animal>();
//        ArrayList<? super Animal> list7 = new ArrayList<Dog>();//报错
//        ArrayList<? super Animal> list8 = new ArrayList<Cat>();//报错
可以看出,泛型的下限只能是该类型的类型及其父类。

一般泛型的上限和下限也是用来参数的传递:

再比如:现已知Object类,String 类,Number类,Integer类,其中Number是Integer的父类

 public static void main(String[] args) {
    Collection<Integer> list1 = new ArrayList<Integer>();
    Collection<String> list2 = new ArrayList<String>();
    Collection<Number> list3 = new ArrayList<Number>();
    Collection<Object> list4 = new ArrayList<Object>();
    
    getElement(list1);
    getElement(list2);//报错
    getElement(list3);
    getElement(list4);//报错
  
    getElement2(list1);//报错
    getElement2(list2);//报错
    getElement2(list3);
    getElement2(list4);
  
}
// 泛型的上限:此时的泛型?,必须是Number类型或者Number类型的子类
public static void getElement1(Collection<? extends Number> coll){}
// 泛型的下限:此时的泛型?,必须是Number类型或者Number类型的父类
public static void getElement2(Collection<? super Number> coll){}

学到这里,我们泛型也就学完了!

相信各位看官都对泛型有了一定了解,在平时开发,比较常见使用在泛型的使用有集合框架中的List和Map。还有很多的应用,期待你慢慢发现!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【ROS2 入门】ROS2 创建工作空间

大家好&#xff0c;我是虎哥&#xff0c;从今天开始&#xff0c;我将花一段时间&#xff0c;开始将自己从ROS1切换到ROS2&#xff0c;在上几篇中&#xff0c;我们一起了解ROS 2中很多基础概念&#xff0c;从今天开始我们逐步就开始利用ROS2的特性进行开发编程了。 工作区&#…

【Linux】基础IO --- 系统级文件接口、文件描述符表、文件控制块、fd分配规则、重定向…

能一个人走的路别抱有任何期待&#xff0c;死不了 文章目录一、关于文件的重新认识二、语言和系统级的文件操作&#xff08;语言和系统的联系&#xff09;1.C语言文件操作接口&#xff08;语言级别&#xff09;1.1 文件的打开方式1.2 文件操作的相关函数1.3 细节问题2.系统级文…

【Go基础】加密算法和数据结构

文章目录一、加密算法1. 对称加密2. 非对称加密3. 哈希算法二、数据结构与算法1. 链表2. 栈3. 堆4. Trie树一、加密算法 1. 对称加密 加密过程的每一步都是可逆的 加密和解密用的是同一组密钥 异或是最简单的对称加密算法 // XOR 异或运算&#xff0c;要求plain和key的长度相…

PHP实现URL长连接转短连接方法总结

依据第二种算法&#xff0c;URL长连接转短连接实现方法如下&#xff1a;语言&#xff1a;PHP5.6服务器环境&#xff1a;LNMP假设&#xff1a;长连接地址&#xff1a;http://www.test.com/index.php短连接地址&#xff1a;http://t.test.com/六位code码第一步&#xff1a;利用sh…

Jupyter使用详解

Jupyter使用详解 本篇文章我们主要介绍Jupyter的使用与配置&#xff0c;本篇文章的主要内容如下&#xff1a; 什么是Jupyter notebookJupyter notebook的安装使用Jupyter notebook 什么是Jupyter notebook&#xff1f; Jupyter Notebook是一个Web应用程序&#xff0c;允许您…

在甲骨文云容器实例(Container Instances)上部署Oracle Linux 8 Desktop加强版(包括Minio,ssh登录等)

甲骨文云推出了容器实例&#xff0c;这是一项无服务器计算服务&#xff0c;可以即时运行容器&#xff0c;而无需管理任何服务器。 今天我们尝试一下通过容器实例部署Oracle Linux 8 Desktop加强版。 加强版里包括&#xff0c;Minio&#xff0c;ssh登录&#xff0c;OCI CLI命令行…

linux基本功系列之-rpm命令实战

文章目录前言&#x1f680;&#x1f680;&#x1f680;一. rpm命令介绍1.1 RPM包介绍1.2 rpm包的优缺点1.3 rpm包获取方式二. 语法格式及常用选项2.1 RPM安装常用参数2.2 rpm格式介绍三. 应用案例3.1 从本地安装软件包3.2 查询lrzsz的包有没有安装3.3 查询命令是哪个包安装的3.…

3.1(完结)Linux扫盲笔记

1. Linux环境下&#xff0c;输入密码&#xff0c;不回回显(*)。 2.普通用户的密码一定不要和root一样&#xff0c;root一定要安全级别更高。具体的添加账户和修改密码的操作&#xff0c;见蛋哥Linux训练营&#xff0c;第2课&#xff0c;30分钟处。 3.在最高权限(root)&#x…

java基础学习 day37 (集合)

集合与数组的区别 长度&#xff1a;数组长度固定&#xff0c;一旦创建完成&#xff0c;就不能改变。集合长度可变&#xff0c;根据添加和删除元素&#xff0c;自动扩容或自动收缩&#xff0c;&#xff08;添加几个元素就扩容多少&#xff0c;删除几个元素就收缩多少&#xff0…

JMeter测试redis性能

JMeter测试redis性能前言插件使用说明前言 针对Redis的性能测试需求本身就比较小众&#xff0c;因为Redis的性能指标在官网已经给出了详细的数据。但是有时候我们仍然需要对redis进行性能测试&#xff0c;例如资源配置需求&#xff0c;参数调优对比&#xff0c;程序优化等场景…

树型结构——二叉数

之前就说过我们的数据结构分为两种&#xff0c;分别是线性结构和非线性结构&#xff0c;我们今天要学的第一种线性结构就是树型结构。 1. 树型结构 树型结构并非我们熟悉的重点&#xff0c;所以在这里只做了解。 概念&#xff1a; 树是一种非线性的数据结构&#xff0c;它是…

【人工智能原理自学】循环:序列依赖问题

&#x1f60a;你好&#xff0c;我是小航&#xff0c;一个正在变秃、变强的文艺倾年。 &#x1f514;本文讲解循环&#xff1a;序列依赖问题&#xff0c;一起卷起来叭&#xff01; 目录一、“序列”二、代码实现一、“序列” 数据除了在空间上可能出现关联性外&#xff0c;也可…

nodejs在线教学网上授课系统vue367

目 录 摘 要 I Abstracts II 目 录 III 第1章 绪论 1 1.1课题背景 1 1.2研究意义 1 1.3研究内容 2 第2章 技术介绍 1 2.1 相关技术 1 1、 node_modules文件夹(有npn install产生) 这文件夹就是在创建完项目后&#xff0c;cd到项目目录执行np…

基于nodejs+vue驾校预约网站管理系统

系统分为用户和管理员&#xff0c;教练三个角色 目 录 第1章 绪论 1 1.1课题背景 1 1.2 背景意义 1 1.3 研究的内容 2 第2章 相关技术 3 第3章 系统分析 5 3.1可行性分析 5 3.2系统性能分析 6 3.3系统流程分析 6 3.3.1操作流程 6 3.3.2信息添加…

Cadence PCB仿真使用Allegro PCB SI生成电源地噪声报告SSN Report及报告导读图文教程

🏡《Cadence 开发合集目录》   🏡《Cadence PCB 仿真宝典目录》 目录 1,概述2,生成报告3,报告导读4,总结1,概述 SSN报告等效的电源和地噪声源报告。本文简单介绍使用Allegro PCB SI生成SSN报告的方法,及其要点导读。 2,生成报告 第1步,选择需要生成报告的网络,…

【绝密】大厂笔试题

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前是C语言学习者 ✈️专栏&#xff1a;C语言刷题 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章对你有帮助的话 欢迎 评论&#x1f4ac; 点赞&…

微信支付账户更换实名认证微信钱包零钱余额还在吗?怎么更换微信钱包实名认证?

原文来源&#xff1a;https://www.caochai.com/article-4119.html 微信支付账户更换实名认证微信钱包零钱余额还在吗&#xff1f; 微信支付账户更换实名认证微信钱包的零钱余额将清空&#xff0c;因为更换微信钱包实名认证的前提条件是微信钱包零钱余额不能大于0元。所以&…

一周学习总结(2022.1.25)

文章目录前言本周任务完成情况1.《Vue.js的设计与实现》2.《计算机网络&#xff1a;自顶向下方法》3.组件库4.青训营笔记5.刷题总结前言 年前给自己定下了一组学习计划&#xff0c;安排了每天需要完成的事情。这里主要记录一下每周任务的完成情况。本周定制的任务主要围绕着《V…

DP初入门

目录 一、前言 二、DP概念 1、最少硬币问题 2、DP的两个特征 三、0/1背包&#xff08;最经典的DP问题&#xff09; 1、小明的背包1&#xff08;lanqiaoOJ题号1174&#xff09; 2、空间优化&#xff1a;滚动数组 1&#xff09;交替滚动 2&#xff09;自我滚动 一、前言…

C语言函数调用详解

所谓函数调用&#xff08;Function Call&#xff09;&#xff0c;就是使用已经定义好的函数。函数调用的一般形式为&#xff1a;functionName(param1, param2, param3 ...);functionName 是函数名称&#xff0c;param1, param2, param3 ...是实参列表。实参可以是常数、变量、表…