11.泛型

文章目录

  • 1 泛型概念
  • 2. 自定义泛型结构
  • 3 泛型方法
  • 4 泛型在继承上的体现
  • 5 通配符的使用

1 泛型概念

所谓泛型就是用标识符标识不确定的类型,详细说就是:定义类或接口时用标识符表示类中某个属性的类型或者是某个方法的返回值及参数类型。泛型将在使用时(例如,继承或实现这个接口,用这个类型声明变量、创建对象时)确定(即传入实际的类型参数,也称为类型实参)。

泛型的优势:

  1. 类型安全性: 泛型允许你在编译时检查类型的一致性。通过指定类型参数,你可以确保代码在运行时不会发生类型转换错误。 (没有泛型的时候用Object,类型太多,有了泛型就能在简化代码的同时规定类型)
  2. 代码重用: 泛型使得你可以编写通用的代码,可以用于多种不同类型的对象,而无需重复编写相似的代码。这样可以减少代码的冗余,提高代码的可维护性和可读性。
  3. 性能优化: 泛型在编译时执行类型检查,而不是在运行时进行。这意味着在运行时不需要额外的类型检查和转换操作,可以提高程序的性能。

2. 自定义泛型结构

  1. 泛型的声明 interface List<T>class GenTest<K,V>

    其中,T,K,V不代表值,而是表示类型。这里使用任意字母都可以。常用T表示,是Type的缩写。

  2. 泛型的实例化:

    当泛型定义的接口或者类实例化时指定泛型具体类型的过程就是泛型的实例化

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

    Iterator<Customer> iterator = customers.iterator();

泛型使用细节:

  1. 泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如:<E1,E2,E3>

  2. 泛型类的构造器定义方式:public GenericClass(){}。而非:public GenericClass<E>(){};

  3. 泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等价于Object。经验:泛型要使用一路都用。要不用,一路都不要用。

    class GenericTest {public static void main(String[] args) {// 1、使用时:类似于Object,不等同于ObjectArrayList list = new ArrayList();// list.add(new Date());//有风险list.add("hello");test(list);// 泛型擦除,编译不会类型检查// ArrayList<Object> list2 = new ArrayList<Object>();// test(list2);//一旦指定Object,编译会类型检查,必须按照Object处理,此处报错 //java: 不兼容的类型: java.util.ArrayList<java.lang.Object>无法转换为java.util.ArrayList<java.lang.String>}public static void test(ArrayList<String> list) {String str = "";for (String s : list) {str += s + ",";}System.out.println("元素:" + str);}
    }
    
  4. 泛型的简化操作:ArrayList<Fruit> flist = new ArrayList<>()类型推断

  5. 泛型的指定中不能使用基本数据类型,可以使用包装类替换

  6. 在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法中不能使用类的泛型,但允许使用和类无关的泛型也就是泛型方法。(因为静态方法早于类实例的创建,并且允许未实例化的类直接调用静态方法,此时泛型未被实例化

  7. 异常类不能是泛型的

  8. 不能使用new E[]。但是可以:E[] elements = (E[])new Object[capacity];参考:ArrayList源码中声明:Object[] elementData,而非泛型参数类型数组。

  9. 父类有泛型,子类可以保留父类泛型、泛型实例化、或者增加泛型等等

    子类不保留父类的泛型:按需实现

    • 没有类型 擦除
    • 具体指定泛型类型

    子类保留父类的泛型:泛型子类

    • 全部保留
    • 部分保留

结论:子类必须是“富二代”,子类除了指定或保留父类的泛型,还可以增加自己的泛型

class Father<T1, T2> {}// 子类不保留父类的泛型
// 1)没有类型 擦除
class Son1 extends Father {}// 等价于class Son extends Father<Object,Object>{// 2)具体指定泛型类型
class Son2 extends Father<Integer, String> {}// 子类保留父类的泛型
// 1)全部保留
class Son3<T1, T2> extends Father<T1, T2> {}
// 2)部分保留
class Son4<T2> extends Father<Integer, T2> {}
// 3)甚至可以增加泛型
class Son5<T2,T3> extends Father<Integer, T2> {}

3 泛型方法

方法,也可以被泛型化,不管此时定义在其中的类是不是泛型类。在泛型方法中可以定义泛型参数,此时,参数的类型就是传入数据的 类型。

泛型方法的格式:(访问权限后面定义泛型)

[访问权限] <泛型> 返回类型 方法名([泛型标识 参数名称]) 抛出的异常

例子一:

public static <T> void fromArrayToCollection(T[] a, Collection<T> c) {for (T o : a) {c.add(o);}
}public static void main(String[] args) {Object[] ao = new Object[100];Collection<Object> co = new ArrayList<Object>();fromArrayToCollection(ao, co);String[] sa = new String[20];Collection<String> cs = new ArrayList<>();fromArrayToCollection(sa, cs);Collection<Double> cd = new ArrayList<>();// 下面代码中T是Double类,但sa是String类型,编译错误。// fromArrayToCollection(sa, cd);// 下面代码中T是Object类型,sa是String类型,可以赋值成功。fromArrayToCollection(sa, co);
}

例子二:

class Creature{}
class Person extends Creature{}
class Man extends Person{}class PersonTest {public static <T extends Person> void test(T t){//要求是Person的子类System.out.println(t);}public static void main(String[] args) {test(new Person());test(new Man());//The method test(T) in the type PersonTest is not //applicable for the arguments (Creature)test(new Creature());//报错!!}
}

4 泛型在继承上的体现

如果B是A的一个子类(子类或者子接口),而G是具有泛型声明的类或接口,G<B>并不是G<A>的子类型!若A是B的子类,则A<T>B<T>的子类

比如:StringObject的子类,但是List<String>并不是List<Object>的子类。

public void testGenericAndSubClass() {Person[] persons = null;Man[] mans = null;// 而 Person[] 是 Man[] 的父类.persons = mans;Person p = mans[0];// 在泛型的集合上List<Person> personList = null;List<Man> manList = null;// personList = manList;(报错)
}

5 通配符的使用

在Java中,类型通配符(Wildcard)是用来表示未知类型的,通常在泛型中使用。它的语法形式是?,可以用在泛型类、泛型方法、以及泛型接口中。

类型通配符有两种常见的形式:

  1. ? extends T: 表示通配符的类型是T或者T的子类型。这个通配符意味着你可以传递T或者T的任何子类型作为参数,但是不能传递T的超类型。

    List<? extends Number> list = new ArrayList<>();
    

    这里list可以指向任何元素是Number或者Number的子类的列表。

  2. ? super T: 表示通配符的类型是T或者T的父类型。这个通配符意味着你可以传递T或者T的任何父类型作为参数,但是不能传递T的子类型。

    List<? super Integer> list = new ArrayList<>();
    

    这里list可以指向任何元素是Integer或者Integer的父类的列表。

    public static void printCollection3(Collection<? extends Person> coll) {//Iterator只能用Iterator<?>或Iterator<? extends Person>.why?Iterator<?> iterator = coll.iterator();while (iterator.hasNext()) {System.out.println(iterator.next());}
    }
    public static void printCollection4(Collection<? super Person> coll) {//Iterator只能用Iterator<?>或Iterator<? super Person>.why?Iterator<?> iterator = coll.iterator();while (iterator.hasNext()) {System.out.println(iterator.next());}
    }
    

使用类型通配符可以增加泛型的灵活性,特别是在处理泛型集合时,因为它允许你编写更通用的代码,而不需要明确知道泛型参数的具体类型。

细节:

  1. 使用类型通配符:?

    比如:List<?> ,Map<?,?>

    List<?>List<String>List<Object>等各种泛型List的父类。

  2. 读取List<?>的对象list中的元素时,永远是安全的,因为不管list的真实类型是什么,它包含的都是Object。

  3. 不能向List<?>中添加元素,因为不知道他的数据类型。但null除外,可以向其添加null值。读取元素正常,读取出来的元素都是Object类型

    将任意元素加入到其中不是类型安全的:

    Collection<?> c = new ArrayList<String>();
    c.add(new Object()); // 编译时错误
    

    因为我们不知道c的元素类型,我们不能向其中添加对象。唯一可以添加的数值式null,另一方面,我们可以调用get()方法并使用其返回值,返回值是一个位置的类型,但是我们知道,他总是一个Object

例子:

public static void main(String[] args) {List<?> list = null;list = new ArrayList<String>();list = new ArrayList<Double>();// list.add(3);//编译不通过使用通配符不能添加元素list.add(null);List<String> l1 = new ArrayList<String>();List<Integer> l2 = new ArrayList<Integer>();l1.add("尚硅谷");l2.add(15);read(l1);read(l2);
}public static void read(List<?> list) {for (Object o : list) {System.out.println(o);
}

注意点:

//注意点1:编译错误:不能用在泛型方法声明上,返回值类型前面<>不能使用?
public static <?> void test(ArrayList<?> list){
}
//注意点2:编译错误:不能用在泛型类的声明上
class GenericTypeClass<?>{
}
//注意点3:编译错误:不能用在创建对象上,右边属于创建集合对象
ArrayList<?> list2 = new ArrayList<?>();

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

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

相关文章

探索React Router:实现动态二级路由

我有一个路由配置的二维数组&#xff0c;想根据这个数组结合路由组件来动态生成路由&#xff0c;应该怎么样实现。在 React Router 6 中渲染二级路由的方式跟 React Router 65相比有一些变化,但核心思路仍然是利用 Route 组件和路由嵌套的方式。下面是具体的步骤: 定义路由数组…

小程序视频如何下载到手机#下载高手

在本文中&#xff0c;我将向您介绍一个工具:下载高手&#xff0c;帮助您轻松下载小程序视频&#xff0c;并将其保存到您的手机中。无论是学习教育类的小程序视频&#xff0c;还是图片素材类的小程序&#xff0c;这些方法都适用于各种类型的小程序。 工具我已经打包好了&#x…

蓝桥杯 — — 完全日期

完全日期 友情链接&#xff1a;完全日期 题目&#xff1a; 思路&#xff1a; 直接从20010101枚举到20211231&#xff0c;然后再判断每一个数是否是一个合法的日期&#xff0c;如果这个日期是合法的&#xff0c;接着判断这个日期的每一个位置上的数字之和是否是一个完全平方数…

GPT-SoVITS声音克隆训练和推理(新手教程,附整合包)

环境: Win10 专业版 GPT-SoVITS-0421 整合包 问题描述: GPT-SoVITS声音克隆如何训练和推理教程 解决方案: Zero-shot TTS: Input a 5-second vocal sample and experience instant text-to-speech conversion.零样本 TTS:输入 5 秒的人声样本并体验即时文本到语音转换…

基于java的CRM客户关系管理系统的设计与实现

本科毕业设计(论文) 题 目&#xff1a; 基于java的CRM客户关系管理系统的设计与实现 专题题目&#xff1a; 说 明 请按以下顺序编排&#xff1a; 封面任务书开题报告中外文摘要及关键词目录正文附录&…

算法学习笔记Day9——动态规划初探

一、介绍 本文解决几个问题&#xff1a;动态规划是什么&#xff1f;解决动态规划问题有什么技巧&#xff1f;如何学习动态规划&#xff1f; 1. 动态规划问题的一般形式就是求最值。动态规划其实是运筹学的一种最优化方法&#xff0c;只不过在计算机问题上应用比较多&#xff…

矩阵连乘算法

矩阵连乘&#xff1a; #include<iostream> #define inf 0x7fffffff using namespace std; int a[256] { 0 };//存储矩阵的行和列 int m[256][256] { 0 };//存储i到j的最少计算次数 int s[256][256] { 0 };//存储i到j的中转站k void m_print(int i, int j) {if (i …

Kubectl常见排查pod问题命令

一.查看命名空间pod及其日志 #查看命名空间pod kubectl get pods -n <命名空间名称> #该命令不加-n命名空间名称&#xff0c;默认是查看default命名空间的pod#查看对应pod的日志kubectl logs -f <pod-name> -n <namespace>#同样的如果查看的是default命名空…

如何在OpenWRT上配置SFTP远程文件传输

如何在OpenWRT上配置SFTP远程文件传输 OpenWRT 是一款广泛使用的开源路由器固件&#xff0c;它能够让普通的家用路由器具备高级路由功能&#xff0c;提供更多自定义和优化选项。本文将介绍如何在OpenWRT上配置SFTP&#xff08;SSH文件传输协议&#xff09;服务&#xff0c;以便…

简单工厂、工厂方法、抽象工厂对比

简单工厂、工厂方法和抽象工厂是三种常见的工厂设计模式&#xff0c;它们在软件设计中各有其独特的应用场景和优缺点。因为三种设计模式都属于工厂模式&#xff0c;在实际应用中可能存在误用的场景&#xff0c;这里对其做下对比&#xff0c;以便更好的理解这三种设计模式。 简…

.NET周刊【4月第2期 2024-04-21】

国内文章 他来了他来了&#xff0c;.net开源智能家居之苹果HomeKit的c#原生sdk【Homekit.Net】1.0.0发布&#xff0c;快来打造你的私人智能家居吧 https://www.cnblogs.com/hezp/p/18142099 三合是一位不喜欢动态编程语言的开发者&#xff0c;对集成米家智能家居到苹果HomeK…

【Ant-Desgin-React 穿梭框】表格穿梭框,树穿梭框的用法

Antd Desgin 穿梭框 普通用法高级用法-表格穿梭框组件高级用法-树穿梭框组件 普通用法 /* eslint-disable no-unused-vars */ import React, { useEffect, useState } from react import { Space, Transfer } from antd// Antd的穿梭框组件Mock数据 const mockData Array.fro…

西安站开营!AI 编码助手通义灵码帮大学生“整活儿”

如何更好地与 AI 为伴&#xff0c;做时代的先进开发者&#xff1f;4 月 17 日&#xff0c;阿里云推出的 AI 编程助手通义灵码与云工开物“高校训练营”走进西安多所高校开启实操培训&#xff0c;结合 AI 辅助编程的发展背景、通义灵码的具体能力和应用实操&#xff0c;帮助在校…

全世界IT人苦竞业久矣!美国FTC宣布全面废除员工竞业协议

2023 年 1 月&#xff0c;美国联邦贸易委员会&#xff08;FTC&#xff09;发布声明称&#xff0c;拟在全国范围禁止用人单位与雇员签订竞业禁止性条款。当地时间 4 月 23 日&#xff0c;FTC 宣布全面禁止所有员工&#xff08;包括高级管理人员&#xff09;签署新的竞业禁止协议…

js生成不同的阅读数分配到每一篇上面,不会因为刷新而变动

js生成不同的阅读数分配到每一篇上面,不会因为刷新而变动 {%- for article in blog.articles -%}<div class"blog-articles__article article">{%- render article-card,article: article,media_height: section.settings.image_height,media_aspect_ratio: a…

xilinx Mailbox 中的ipi message地址计算方式

适用于openAmp mailbox ipi id对应的ipi message地址计算方式 官方openamp硬件配置解析 OpenAMP Base Hardware Configurations - Xilinx Wiki - Confluence openamp官方设备树 meta-openamp/meta-xilinx-tools/recipes-bsp/device-tree/files/zynqmp-openamp.dtsi at rel-v2…

SpringBoot+Vue开发记录(四)

说明&#xff1a; 本篇文章的主要内容是软件架构以及项目的前端Vue创建 一、软件架构 我道听途说的&#xff0c;听说这个东西很关键很重要什么的。 软件架构&#xff08;software architecture&#xff09;是一个系统的草图,是一系列相关的抽象模式&#xff0c;用于指导大型软…

AI商业智能的一些分享

本文主要讲AI商业相关的&#xff08;特别是营销相关的&#xff09;一些知识点&#xff0c;比较零散。 简单总结 AI商业智能&#xff1a; 1&#xff09;将人员经验抽象化为算法规则&#xff0c; 2)打造数据驱动的精益运营能力&#xff0c; 3)长期保持价格竞争力并将商品毛利让…

Day 21 LAMP架构和DNS域名

LAMP架构简介 针对不同的后端开发语言&#xff0c;使用不同的架构&#xff0c;后端项目开发语言有&#xff1a;Java&#xff0c;PHP&#xff0c;Python...... 针对于PHP项目 LAMP架构 LinuxApacheMysql/MariadbPhp LNMP架构 LinuxNginxMysql/MariadbPhp 针对于Java项目 w…

国密SSL证书在等保、关保、密评合规建设中的应用

在等保、关保、密评等合规建设中&#xff0c;网络和通信安全方面的建设是非常重要的部分&#xff0c;需要实现加密保护和安全认证&#xff0c;确保传输数据机密性、完整性以及通信主体可信认证。国密SSL证书应用于等保、关保和密评合规建设中&#xff0c;不仅能够提升网络信息系…