什么叫泛型?有什么作用?

2023-11-03

作者:Java3y
链接:https://www.zhihu.com/question/272185241/answer/366129174
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
 

一、什么是泛型?

Java泛型设计原则:只要在编译时期没有出现警告,那么运行时期就不会出现ClassCastException异常.

泛型:把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊的类型

参数化类型:

  • 把类型当作是参数一样传递
  • <数据类型> 只能是引用类型

相关术语:

  • ArrayList<E>中的E称为类型参数变量
  • ArrayList<Integer>中的Integer称为实际类型参数
  • 整个称为ArrayList<E>泛型类型
  • 整个ArrayList<Integer>称为参数化的类型ParameterizedType

二、为什么需要泛型

早期Java是使用Object来代表任意类型的,但是向下转型有强转的问题,这样程序就不太安全

首先,我们来试想一下:没有泛型,集合会怎么样

  • Collection、Map集合对元素的类型是没有任何限制的。本来我的Collection集合装载的是全部的Dog对象,但是外边把Cat对象存储到集合中,是没有任何语法错误的。
  • 把对象扔进集合中,集合是不知道元素的类型是什么的,仅仅知道是Object。因此在get()的时候,返回的是Object。外边获取该对象,还需要强制转换

有了泛型以后:

  • 代码更加简洁【不用强制转换】
  • 程序更加健壮【只要编译时期没有警告,那么运行时期就不会出现ClassCastException异常】
  • 可读性和稳定性【在编写集合的时候,就限定了类型】

2.1有了泛型后使用增强for遍历集合

在创建集合的时候,我们明确了集合的类型了,所以我们可以使用增强for来遍历集合!

       //创建集合对象
        ArrayList<String> list = new ArrayList<>();

        list.add("hello");
        list.add("world");
        list.add("java");

        //遍历,由于明确了类型.我们可以增强for
for (String s : list) {
            System.out.println(s);
        }

三、泛型基础

3.1泛型类

泛型类就是把泛型定义在类上,用户使用该类的时候,才把类型明确下来....这样的话,用户明确了什么类型,该类就代表着什么类型...用户在使用的时候就不用担心强转的问题,运行时转换异常的问题了。

  • 在类上定义的泛型,在类的方法中也可以使用!
/*
    1:把泛型定义在类上
    2:类型变量定义在类上,方法中也可以使用
 */
public class ObjectTool<T> {
private T obj;

public T getObj() {
return obj;
    }

public void setObj(T obj) {
this.obj = obj;
    }
}
  • 测试代码:

用户想要使用哪种类型,就在创建的时候指定类型。使用的时候,该类就会自动转换成用户想要使用的类型了。

public static void main(String[] args) {
        //创建对象并指定元素类型
        ObjectTool<String> tool = new ObjectTool<>();

        tool.setObj(new String("钟福成"));
        String s = tool.getObj();
        System.out.println(s);


        //创建对象并指定元素类型
        ObjectTool<Integer> objectTool = new ObjectTool<>();
        /**
         * 如果我在这个对象里传入的是String类型的,它在编译时期就通过不了了.
         */
        objectTool.setObj(10);
int i = objectTool.getObj();
        System.out.println(i);
    }

3.2泛型方法

前面已经介绍了泛型类了,在类上定义的泛型,在方法中也可以使用.....

现在呢,我们可能就仅仅在某一个方法上需要使用泛型....外界仅仅是关心该方法,不关心类其他的属性...这样的话,我们在整个类上定义泛型,未免就有些大题小作了。

  • 定义泛型方法....泛型是先定义后使用的
   //定义泛型方法..
public <T> void show(T t) {
        System.out.println(t);

    }
  • 测试代码:

用户传递进来的是什么类型,返回值就是什么类型了

public static void main(String[] args) {
        //创建对象
        ObjectTool tool = new ObjectTool();

        //调用方法,传入的参数是什么类型,返回值就是什么类型
        tool.show("hello");
        tool.show(12);
        tool.show(12.5);

    }

3.3泛型类派生出的子类

前面我们已经定义了泛型类,泛型类是拥有泛型这个特性的类,它本质上还是一个Java类,那么它就可以被继承

那它是怎么被继承的呢??这里分两种情况

  1. 子类明确泛型类的类型参数变量
  2. 子类不明确泛型类的类型参数变量

3.3.1子类明确泛型类的类型参数变量

  • 泛型接口
/*
    把泛型定义在接口上
 */
public interface Inter<T> {
public abstract void show(T t);

}
  • 实现泛型接口的类.....
/**
 * 子类明确泛型类的类型参数变量:
 */

public class InterImpl implements Inter<String> {
    @Override
public void show(String s) {
        System.out.println(s);

    }
}

3.3.2子类不明确泛型类的类型参数变量

  • 当子类不明确泛型类的类型参数变量时,外界使用子类的时候,也需要传递类型参数变量进来,在实现类上需要定义出类型参数变量
/**
 * 子类不明确泛型类的类型参数变量:
 *      实现类也要定义出<T>类型的
 *
 */
public class InterImpl<T> implements Inter<T> {

    @Override
public void show(T t) {
        System.out.println(t);

    }
}

测试代码:

public static void main(String[] args) {
        //测试第一种情况
        //Inter<String> i = new InterImpl();
        //i.show("hello");

        //第二种情况测试
        Inter<String> ii = new InterImpl<>();
        ii.show("100");

    }

值得注意的是:

  • 实现类的要是重写父类的方法,返回值的类型是要和父类一样的!
  • 类上声明的泛形只对非静态成员有效

3.4类型通配符

为什么需要类型通配符????我们来看一个需求.......

现在有个需求:方法接收一个集合参数,遍历集合并把集合元素打印出来,怎么办?

  • 按照我们没有学习泛型之前,我们可能会这样做:
public void test(List list){


for(int i=0;i<list.size();i++){

        System.out.println(list.get(i));

    }
}

上面的代码是正确的,只不过在编译的时候会出现警告,说没有确定集合元素的类型....这样是不优雅的...

  • 那我们学习了泛型了,现在要怎么做呢??有的人可能会这样做:
public void test(List<Object> list){


for(int i=0;i<list.size();i++){

        System.out.println(list.get(i));

    }
}

这样做语法是没毛病的,但是这里十分值得注意的是:该test()方法只能遍历装载着Object的集合!!!

强调:泛型中的<Object>并不是像以前那样有继承关系的,也就是说List<Object>List<String>是毫无关系的!!!!

那现在咋办???我们是不清楚List集合装载的元素是什么类型的,List<Objcet>这样是行不通的........于是Java泛型提供了类型通配符 ?

所以代码应该改成这样:

public void test(List<?> list){


for(int i=0;i<list.size();i++){

        System.out.println(list.get(i));

    }
}

?号通配符表示可以匹配任意类型,任意的Java类都可以匹配.....

现在非常值得注意的是,当我们使用?号通配符的时候:就只能调对象与类型无关的方法,不能调用对象与类型有关的方法。

记住,只能调用与对象无关的方法,不能调用对象与类型有关的方法。因为直到外界使用才知道具体的类型是什么。也就是说,在上面的List集合,我是不能使用add()方法的。因为add()方法是把对象丢进集合中,而现在我是不知道对象的类型是什么。


3.4.1设定通配符上限

首先,我们来看一下设定通配符上限用在哪里....

现在,我想接收一个List集合,它只能操作数字类型的元素【Float、Integer、Double、Byte等数字类型都行】,怎么做???

我们学习了通配符,但是如果直接使用通配符的话,该集合就不是只能操作数字了。因此我们需要用到设定通配符上限

   List<? extends Number>

上面的代码表示的是:List集合装载的元素只能是Number的子类或自身

public static void main(String[] args) {


        //List集合装载的是Integer,可以调用该方法
        List<Integer> integer = new ArrayList<>();
        test(integer);

        //List集合装载的是String,在编译时期就报错了
        List<String> strings = new ArrayList<>();
        test(strings);

    }


public static void test(List<? extends Number> list) {

    }

3.4.2设定通配符下限

既然上面我们已经说了如何设定通配符的上限,那么设定通配符的下限也不是陌生的事了。直接来看语法吧

   //传递进来的只能是Type或Type的父类
    <? super Type>

设定通配符的下限这并不少见,在TreeSet集合中就有....我们来看一下

public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator));
    }

那它有什么用呢??我们来想一下,当我们想要创建一个TreeSet<String>类型的变量的时候,并传入一个可以比较String大小的Comparator。

那么这个Comparator的选择就有很多了,它可以是Comparator<String>,还可以是类型参数是String的父类,比如说Comparator<Objcet>....

这样做,就非常灵活了。也就是说,只要它能够比较字符串大小,就行了

值得注意的是:无论是设定通配符上限还是下限,都是不能操作与对象有关的方法,只要涉及到了通配符,它的类型都是不确定的!

 

 

 

经评论补充:在泛型的上限和下限中有一个原则:PECS(Producer Extends Consumer Super)

 

书上是这样写的:

  • 带有子类限定的可以从泛型读取【也就是--->(? extend T)】-------->Producer Extends
  • 带有超类限定的可以从泛型写入【也就是--->(? super T)】-------->Consumer Super

也有相关博文写得很好:

 

3.5通配符和泛型方法

大多时候,我们都可以使用泛型方法来代替通配符的.....

   //使用通配符
public static void test(List<?> list) {

    }

    //使用泛型方法
public <T> void  test2(List<T> t) {

    }

上面这两个方法都是可以的.....那么现在问题来了,我们使用通配符还是使用泛型方法呢??

原则:

  • 如果参数之间的类型有依赖关系,或者返回值是与参数之间有依赖关系的。那么就使用泛型方法
  • 如果没有依赖关系的,就使用通配符,通配符会灵活一些.

3.6泛型擦除

泛型是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,即挡住向集合中插入非法数据。但编译器编译完带有泛形的java程序后,生成的class文件中将不再带有泛形信息,以此使程序运行效率不受到影响,这个过程称之为“擦除”。

3.6.1兼容性

JDK5提出了泛型这个概念,但是JDK5以前是没有泛型的。也就是泛型是需要兼容JDK5以下的集合的。

当把带有泛型特性的集合赋值给老版本的集合时候,会把泛型给擦除了。

值得注意的是:它保留的就类型参数的上限。

       List<String> list = new ArrayList<>();

        //类型被擦除了,保留的是类型的上限,String的上限就是Object
        List list1 = list;

如果我把没有类型参数的集合赋值给带有类型参数的集合赋值,这又会怎么样??

       List list = new ArrayList();
        List<String> list2 = list;

它也不会报错,仅仅是提示“未经检查的转换”


四、泛型的应用

当我们写网页的时候,常常会有多个DAO,我们要写每次都要写好几个DAO,这样会有点麻烦。

 

 

那么我们想要的效果是什么呢??只写一个抽象DAO,别的DAO只要继承该抽象DAO,就有对应的方法了。

要实现这样的效果,肯定是要用到泛型的。因为在抽象DAO中,是不可能知道哪一个DAO会继承它自己,所以是不知道其具体的类型的。而泛型就是在创建的时候才指定其具体的类型。

  • 抽象DAO
public abstract class BaseDao<T> {

    //模拟hibernate....
private Session session;
private Class clazz;


    //哪个子类调的这个方法,得到的class就是子类处理的类型(非常重要)
public BaseDao(){
        Class clazz = this.getClass();  //拿到的是子类
        ParameterizedType  pt = (ParameterizedType) clazz.getGenericSuperclass();  //BaseDao<Category>
        clazz = (Class) pt.getActualTypeArguments()[0];
        System.out.println(clazz);

    }


public void add(T t){
        session.save(t);
    }

public T find(String id){
return (T) session.get(clazz, id);
    }

public void update(T t){
        session.update(t);
    }

public void delete(String id){
        T t = (T) session.get(clazz, id);
        session.delete(t);
    }

}
  • 继承抽象DAO,该实现类就有对应的增删改查的方法了。

CategoryDao

public class CategoryDao extends BaseDao<Category> {

}

BookDao

public class BookDao extends BaseDao<Book> {


}

五、最后

泛型的基础就介绍到这里了,如果以后有需要的话再进行深入研究吧~如果觉得该文章帮助到你,不妨点个赞,关注公众号一波~

 

 

参考资料:

  • Core Java
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

什么叫泛型?有什么作用? 的相关文章

  • 【JavaWeb】MVC模式和JSP开发模型

    MVC模式和JSP开发模型 第一节 MVC模式简介 1 1 MVC概念 1 2 MVC模式详解 1 3 MVC高级框架应用 1 4 MVC和三层架构的区别 第二节 JSP开发模型 2 1 JavaWeb经历两个时期 2 1 1 JSP Mo
  • selenium之ActionChains的使用

    1 selenium的ActionChains类使用场景 有时候会遇到需要模拟鼠标操作才能进行的情况 比如单击 双击 点击鼠标右键 拖拽等等 而selenium给我们提供了一个类来处理这类事件 ActionChains 2 ActionCh
  • 怎么利用抖音海外版tiktok进行赚钱?

    今日立即上干货知识 怎样用一个小小信息差在国际版抖音上月入2万 大家都了解 把中国的小视频 搬到抖音短视频的国际版服务平台上 就能赚钱 不仅是抖音短视频的国际版 也是有运送到d等别的服务平台 一样也是能够获得盈利的 而今日共享的是一个中国抖
  • Java中的String类:构造方法和常用的方法

    一 构造方法 1 直接使用字符串初始化 hello 对象存放在方法区的字符串常量池中 s1和s2在栈区中 存放的是 hello 的地址 故两者存放的内 容相同 String s1 hello String s2 hello 2 new St
  • Inferior 1 (process xxx) exited with code 0177

    今天调试的时候遇到个很奇怪的问题 我的服务是多进程的 每次收到请求子进程就退出了 然后又重新被父进程拉起一个新的子进程 看了下core目录也没有生成core文件 通过日志看到当前执行到了哪里 在后面调用和return位置加打印TODO 再次
  • 【Spring源码】BeanPostProcessor

    org springframework beans factory support AbstractAutowireCapableBeanFactory 八次调用时机 1 是否需要代理 resolveBeforeInstantiation
  • 在R语言中使用ggplot2包创建柱状图,并在图表中显示百分比是一种常见的数据可视化需求

    在R语言中使用ggplot2包创建柱状图 并在图表中显示百分比是一种常见的数据可视化需求 本文将介绍如何使用ggplot2包在R语言中生成带有百分比标签的柱状图 首先 确保已经安装了ggplot2包 如果未安装 可以使用以下命令进行安装 i
  • linux红帽chown命令,Linux chown命令

    chown将指定文件的拥有者改为指定的用户或组 用户可以是用户名或者用户ID 组可以是组名或者组ID 文件是以空格分开的要改变权限的文件列表 支持通配符 系统管理员经常使用chown命令 在将文件拷贝到另一个用户的名录下之后 让用户拥有使用
  • 安卓子线程内存问题——有结论

    问题描述 有一套C 库 通过JNI被安卓应用调用 应用中在主线程 UI现场 调用一函数正常 在子线程中调用该函数会导致APP崩溃 APP崩溃时报错信息如下 E libsigchain exiting due to SIG DFL handl
  • eBay架构的思想金矿

    2008年01月24日 星期四 11 53 P M 英文来源 http www manageability org blog stuff about ebays architecture An accurate way of knowing
  • 数组对象根据id字段去重

    数组对象根据id字段去重
  • iOS App 上架流程图文教学

    引言 在上架App 之前必须先准备好开发者帐号 但申请开发者帐号因法兰克早在之前已经申请好了 故就跳过此步骤 直接从产生凭证到上传App开始讲起 首先 要将自己辛苦写好的App 送审的话 则要依序做完下列几件事情即可 在开发者后台产生 ce
  • Go 语言中 = 和 := 有什么区别

    是赋值 是声明变量并赋值 使用必须使用先var声明例如 var a a 100 或 var b 100 或 var c int 100 是声明并赋值 并且系统自动推断类型 不需要var关键字 d 100
  • 编写一个"banner"函数,该函数的输入为大写字母

    编写一个 banner 函数 该函数的输入为大写字母 题目 编写一个 banner 函数 该函数的输入为大写字母 输出为一个字符数组 该数组以图像化的方式表示该字母 编程 珠玑 上提到当要 输入 的 数据 很多 且没有 规律 时 可以 考虑
  • 什么是代理?Java 中如何使用代理

    什么是代理 Java 中如何使用代理 什么是代理 代理是一种设计模式 它允许一个对象 代理对象 代表另一个对象 真实对象 进行一些操作 代理对象和真实对象有着相同的接口 因此代理对象可以替代真实对象的位置 而不会对客户端代码产生影响 代理对
  • 使用nginx作为HTTPS正向代理服务器(七层透传代理、中间人代理)

    前言 在讲解nginx正向代理https之前 我们先来解答几个小疑问 1 nginx是什么 Java同学肯定知道apache服务器 一个很牛 但是也很庞大的web服务器 能当web服务器的不仅仅只有apache 还有一个小巧轻快 高性能的家
  • Angular: Program ng failed to run No application is associated

    今天 搭建 Angular CLI 框架的时候 遇见了一个奇怪的问题 当我将 Angular CLI 搭建完成以后 我在 Windows PowerShell 和命令提示符上输入 ng 命令是工作正常的 但在 VSCode PowerShe
  • 区块链:Solidity值类型(Solidity 字典/映射 Mappings)

    语法 mapping KeyType gt ValueType 字典 映射其实就是一个一对一键值存储关系 age 28 height 172 name wt 同一个映射中 可以有多个相同的值 但是键必须具备唯一性 pragma solidi
  • 统计学基础

    数理统计简介 统计推断的三大问题 1 抽样分布 精确的与近似的 2 参数估计 点估计与区间估计 3 假设检验 1 1数理统计中的基本概念 总体与个体 在一个统计问题的研究中 我们把研究对象的全体构成的集合称为总体 集合中的每一个对象 元素
  • Android基础笔记:(2)四大组件 Service 详解【更新结束】

    目录 一 Service简介 二 Service分类 1 Started Service 2 Bound Service 3 启动Service回调的方法 4 通过代码还原Service生命周期 三 使用IntentService 1 为什

随机推荐

  • 【算法】常用的排序方法

    这里总结了几种排序方法 直接有函数可以调用 sort头函数 sort H ifndef SORT H define SORT H void InsertSort int arry int n void DubbleSort int arry
  • JAVA中的抽象类和接口应用_java编程中抽象类与接口的区别和应用场景

    随着互联网的不断发展 越来越多的程序员都开始学习java编程语言 而进我们就一起来了解一下 java编程中抽象类与接口的区别和应用场景 一 抽象类 抽象类体现了数据抽象的思想 不然呢 是实现多态的一种机制 抽象类定义了一组抽象的方法 至于这
  • 论文阅读 - Large-scale weakly-supervised pre-training for video action recognition

    文章目录 1 概述 2 数据的收集方式 3 使用的模型 4 预训练时的一系列问题 4 1 预训练的数据是不是越多越好 4 2 用于预训练的模型是不是越大越好 4 3 预训练数据的标签种类和数量是不是越多越好 4 4 用于预训练的每个vide
  • 时间复杂度-线性对数时间nlogn的一些研究

    文章目录 排序算法的时间复杂度 二叉树与 n l o g 2 n
  • Rabbitmq的消息转换器

    Spring会把你发送的消息序列化为字节发送给MQ 接收消息的时候 还会把字节反序列化为Java对象 只不过 默认情况下Spring采用的序列化方式是JDK序列化 众所周知 JDK序列化存在下列问题 数据体积过大 有安全漏洞 可读性差 默认
  • 微信公众号H5页面(vue)跳转至微信小程序页面方案总结

    微信公众号H5跳转微信小程序方案总结 1 需求背景 最近由于发挥小程序的性能与用户体验优势 决定将微信公众号的部分功能跳转至小程序相关模块 解决方案 注意开放对象如下 已认证的服务号 服务号绑定 JS接口安全域名 下的网页可跳转任意合法合规
  • 使用腾讯云轻量服务器Matomo应用模板建网站流量统计系统

    腾讯云百科分享使用腾讯云轻量应用服务器Matomo应用模板搭建网站流量统计系统 Matomo 是一款开源的网站数据统计软件 可以用于跟踪 分析您的网站的流量 同时充分保障数据安全性 隐私性 该镜像基于 CentOS 7 6 64位操作系统
  • 【性能】Android中的内存溢出(Out Of Memory,OOM)

    性能 Android中的内存溢出 Out Of Memory OOM 1 JVM内存区域介绍 2 OOM形成的原因 3 造成OOM的有哪些 3 1 从JVM的角度 3 2 从具体使用角度 3 2 1 内存泄漏导致的内存溢出 3 2 2 资源
  • 【设计】低压差稳压器(LDO)的设计分析

    本简短教程介绍了一些常用的LDO 相关术语 以及一些基本概念 如压差 裕量电压 静态电流 接地电流 关断电流 效率 直流输入电压和负载调整率 输入电压和负载瞬态响应 电源抑制比 PSRR 输出噪声和精度 同时 为了方便理解 文中采用了示例和
  • WebStorm 初步使用 & HTML5 学习报告

    WebStorm 初步使用 WebStorm介绍 WebStorm是Jetbrains公司旗下的一款JavaScript开发工具 因其界面简洁 操作方便 被广大国内JS开发者誉为 Web前端开发神器 WebStorm具有智能代码补全 代码格
  • 宝塔面板部署Java项目

    宝塔面板部署Java项目 使用宝塔面板里面的 Java 项目管理器来进行部署 首先注意 1 tomcat7 8 9使用的端口依次是8081 8082 8083 安装的那个版本Tomcat就 开启对应的端口 2 该管理器项目不是部署在tomc
  • 【JMeter】RSA加密传参处理方法

    问题 登录请求参数中输入了正确的账号密码 响应结果报错 用户名或密码错误 原因分析 密码需要以RSA加密的方式传参 不能明文 解决方法 在该登录接口下新增一个 B e a n
  • UDP的抓包和网络协议

    背景 一直都在wireshar抓包 但是经常看的都是应用的报文 没有关注到udp的本身传输协议 所以花了一点时间查看了一下UDP的传输协议是什么样的 报文 这个是之前抓包的udp 的协议报文然后对应协议的字段进行协议 目前查看的话还是比较清
  • [编程题] 等差数列

    如果一个数列S满足对于所有的合法的i 都有S i 1 S i d 这里的d也可以是负数和零 我们就称数列S为等差数列 小易现在有一个长度为n的数列x 小易想把x变为一个等差数列 小易允许在数列上做交换任意两个位置的数值的操作 并且交换操作允
  • linux下使用socat

    其实网络协议比较简单 一般都是一个固定的套路 传 输 包 传 输 的 数
  • MES系统各部门评估建议

    生产部 如果期望通过项目推动来解决库存和交期的问题 需要首先针对我们最大的短板和痛点 供应商交期和品质 有针对性的改善 否则系统上了后也会因短板没有解决 无法实现最终目的 如果是为了解决目前制造部各单元计划协同性及准确性问题 以及针对急单插
  • Linux网络文件共享服务(一)存储类型和文件传输协议FTP

    成功不易 加倍努力 网络文件共享服务 本章总目录 1 存储类型 1 1 DAS存储 1 2 NAS存储 1 3 SAN存储 1 4 三种存储比较 2 文件传输协议 FTP 2 1 FTP工作原理介绍 2 2 常见 FTP 相关软件 2 3
  • 谈谈To B业务的难点

    最近有个说法 中国互联网的新增长点是 To B业务 而一个经常被提及的事实是 中美互联网巨头对比 在To C业务上的收益和市值近乎并驾齐驱 虽有差距 但至少是可以相提并论的 而在To B业务上 美国巨头的市场规模 比起中国的同类公司 高两个
  • STM32CubeMX之emWin移植

    前面两章介绍了SDRAM和LTDC的使用 本篇文章将介绍emWin移植到STM32 硬件环境 STM32F429IGT6 软件环境 STM32CubeMX v5 5 0 HAL库版本 STM32CubeF4 Firmware Package
  • 什么叫泛型?有什么作用?

    作者 Java3y 链接 https www zhihu com question 272185241 answer 366129174 来源 知乎 著作权归作者所有 商业转载请联系作者获得授权 非商业转载请注明出处 一 什么是泛型 Jav