泛型aaaaa

1、泛型的概述:

1.1 泛型的由来

根据《Java编程思想》中的描述,泛型出现的动机:

有很多原因促成了泛型的出现,而最引人注意的一个原因,就是为了创建容器类。

泛型的思想很早就存在,如C++中的模板(Templates)。模板的精神:参数化类型

1.2 基本概述

  • 泛型的本质就是"参数化类型"。一提到参数,最熟悉的就是定义方法的时候需要形参,调用方法的时候,需要传递实参。那"参数化类型"就是将原来具体的类型参数化
  • 泛型的出现避免了强转的操作,在编译器完成类型转化,也就避免了运行的错误。

1.3 泛型的目的

  • Java泛型也是一种语法糖,在编译阶段完成类型的转换的工作,避免在运行时强制类型转换而出现ClassCastException,类型转化异常。

1.4 实例

public void test01() {// 创建一个可以存储任何类型对象的ArrayListArrayList<Object> objects = new ArrayList<>();// 创建一个专门用于存储String对象的ArrayListArrayList<String> strings = new ArrayList<>();// 下面的代码将导致编译错误,因为Java泛型不支持协变// objects = strings; // 错误:类型不匹配// 创建一个专门用于存储Cat对象的ArrayListArrayList<Cat> cats = new ArrayList<>();cats.add(new Cat());// 从ArrayList<Cat>中安全地取出Cat对象Cat cat = cats.get(0); // 正确:类型安全// 下面的代码将导致编译错误,因为Dog不是Cat的子类// cats.add(new Dog()); // 错误:类型不匹配// 使用Object作为类型参数的ArrayList可以存储任何类型的对象ArrayList<Object> cats2 = new ArrayList<>();// 向cats2添加Dog对象是允许的,因为Dog是Object的子类cats2.add(new Dog());// 从ArrayList<Object>中取出对象时需要进行类型转换// 但是这里直接转换为Cat类型是不安全的,因为cats2中实际上是Dog类型// Cat c = (Cat) cats2.get(0); // 这将导致ClassCastException
}

JDK 1.5时增加了泛型,在很大的程度上方便在集合上的使用。

  • 不使用泛型:
public static void main(String[] args) {       List list = new ArrayList();        list.add(11);        list.add("ssss");        for (int i = 0; i < list.size(); i++) {         System.out.println((String)list.get(i));        }    
}

因为list类型是Object。所以int,String类型的数据都是可以放入的,也是都可以取出的。但是上述的代码,运行的时候就会抛出类型转化异常,这个相信大家都能明白。

  • 使用泛型:
public static void main(String[] args) {        List<String> list = new ArrayList();        list.add("hahah");        list.add("ssss");        for (int i = 0; i < list.size(); i++) {         System.out.println((String)list.get(i));        }    
}

在上述的实例中,我们只能添加String类型的数据,否则编译器会报错。

2、泛型的使用

泛型的三种使用方式:泛型类泛型方法泛型接口

2.1 泛型类

  • 泛型类概述:把泛型定义在类上
  • 定义格式:
public class 类名 <泛型类型1,...> {    }
  • 注意事项:泛型类型必须是引用类型(非基本数据类型)

2.2 泛型方法

  • 泛型方法概述:把泛型定义在方法上
  • 定义格式:
public <泛型类型> 返回类型 方法名(泛型类型 变量名) {    }
  • 注意要点:
    • 方法声明中定义的形参只能在该方法里使用,而接口、类声明中定义的类型形参则可以在整个接口、类中使用。当调用fun()方法时,根据传入的实际对象,编译器就会判断出类型形参T所代表的实际类型。
class Demo{    public <T> T fun(T t){   // 可以接收任意类型的数据    return t ;     // 直接把参数返回    }  
}
public class GenericsDemo26{    public static void main(String args[]){      Demo d = new Demo(); // 实例化Demo对象      String str = d.fun("汤姆");// 传递字符串      int i = d.fun(30) ;  // 传递数字,自动装箱      System.out.println(str); // 输出内容      System.out.println(i);  // 输出内容    }  
}

2.3 泛型接口

  • 泛型接口概述:把泛型定义在接口
  • 定义格式:
public interface 接口名<泛型类型> {    }
  • 实例:
/** * 泛型接口的定义格式:        
修饰符  interface 接口名<数据类型> {
} */
public interface Inter<T> {    public abstract void show(T t) ;
} 
/** * 子类是泛型类 */
public class InterImpl<E> implements Inter<E> {    @Override    public void show(E t) {        System.out.println(t);    }
}  
Inter<String> inter = new InterImpl<String>() ;
inter.show("hello") ;

2.4 源码中泛型的使用,下面是List接口和ArrayList类的代码片段。

//定义接口时指定了一个类型形参,该形参名为E
public interface List<E> extends Collection<E> {   //在该接口里,E可以作为类型使用   public E get(int index) {}   public void add(E e) {} 
} 
//定义类时指定了一个类型形参,该形参名为E
public class ArrayList<E> extends AbstractList<E> implements List<E> {   //在该类里,E可以作为类型使用   public void set(E e) {   }
}

2.5 泛型类派生子类

父类派生子类的时候不能在包含类型形参,需要传入具体的类型

  • 错误的方式:
public class A extends Container<K, V> {}
  • 正确的方式:
public class A extends Container<Integer, String> {}
  • 也可以不指定具体的类型,系统就会把K,V形参当成Object类型处理
public class A extends Container {}

2.6 泛型构造器

  • 构造器也是一种方法,所以也就产生了所谓的泛型构造器。
  • 和使用普通方法一样没有区别,一种是显示指定泛型参数,另一种是隐式推断
public class Person {    public <T> Person(T t) {        System.out.println(t);    }    
}
public static void main(String[] args) {    new Person(22);// 隐式    new <String> Person("hello");//显示
}
  • 特殊说明:

    • 如果构造器是泛型构造器,同时该类也是一个泛型类的情况下应该如何使用泛型构造器:因为泛型构造器可以显式指定自己的类型参数(需要用到菱形,放在构造器之前),而泛型类自己的类型实参也需要指定(菱形放在构造器之后),这就同时出现了两个菱形了,这就会有一些小问题,具体用法再这里总结一下。 以下面这个例子为代表
  public class Person<E> {public <T> Person(T t) {System.out.println(t);}}正确用法: public static void main(String[] args) {    Person<String> person = new Person("sss");}

PS:编译器会提醒你怎么做的

2.7 高级通配符

在这里插入图片描述

Wildcards 通配符

通配符即指 ?在泛型代码中,? 表示未知类型。通配符可用于多种情况:

  1. 作为参数、字段或局部变量的类型,有时作为返回类型(但请避免这样做)。
  2. 通配符从不用作泛型方法调用、泛型类实例创建或超类型的类型参数。

1.用法

通配符分为 3 种:

  1. <? extends T> 上界通配符:

    上界通配符顾名思义,表示的是类型的上界【包含自身】,因此通配的参数化类型可能是T或T的子类。
    它表示集合中的所有元素都是Animal类型或者其子类

    用关键字extends来实现,实例化时,指定类型实参只能是extends后类型的子类或其本身。

    例如:

   //Cat是其子类    List<? extends Animal> list = new ArrayList<Cat>();  

这样就确定集合中元素的类型,虽然不确定具体的类型,但最起码知道其父类。然后进行其他操作。

  1. <? super 子类>下界通配符:
    如:<? super Integer>

    假设你要编写一个将 Integer 对象放入列表的方法。为了最大限度地提高灵活性,希望该方法适用于
    List、List和 List 任何可以保存 Integer 值的东西。

public static void addNumbers(List<? super Integer> list) {for (int i = 1; i <= 10; i++) {list.add(i);}
} 
  1. <?> 无界通配符:

    任意类型,如果没有明确,那么就是Object以及任意的Java类了

    无界通配符用<?>表示,?代表了任何的一种类型,能代表任何一种类型的只有null(Object本身也算是一种类型,但却不能代表任何一种类型,所以List和List的含义是不同的,前者类型是Object,也就是继承树的最上层,而后者的类型完全是未知的)

    如 List<?> 这表示未知类型的列表,一般有两种情况下无界通配符是有用的:

    你正在编写可以使用 Object类中提供的功能实现的方法
    当代码使用不依赖于类型参数的泛型类中的方法时。例如,List.size或 List.clear。事实上,Class<?> 之所以如此常用,是因为 Class中的大多数方法都不依赖于 T。

如何理解这句话的意思呢?来看一个例子:

  public static void printList(List<Object> list) {for (Object elem : list)System.out.println(elem + " ");System.out.println();}

printList 的意图是想打印任何类型的列表,但是它没有达到目标,其只打印了 Object 实例的列表。
它不能打印 List < Integer >、List < String>、List< Double>等,因为它们不是 List < Object> 的子类型。

  public class JestTestMain {public static void main(String[] args) {List<String> names= Lists.newArrayList();names.add("张三");names.add("张三1");names.add("张三2");printList(names);}public static void printList(List<?> list) {for (Object elem : list)System.out.println(elem + " ");System.out.println();}}
Java 中泛型 T 和 ? 的区别

泛型中 T 类型变量 和 ? 通配符 区别

  1. 定义不同

    • 类型变量(T):是一个泛型类或方法中使用的占位符,用于指定一个具体的类型,这个类型在类或方法被实例化或调用时确定。
    • 通配符(?):用于表示未知的类型,提供了一种方式来使用泛型而不必指定具体的类型。
  2. 使用范围不同

    • 通配符(?):通常用于方法参数、字段类型、局部变量类型,有时也用作返回类型,尽管这不是推荐的做法。通配符的使用场景是当你不需要关心具体的类型,而只想使用泛型方法或类提供的功能时。
    • 类型变量(T):用于声明泛型类的类型参数或泛型方法的类型参数。类型变量的使用场景是当你需要编写可重用且类型安全的代码,并且需要在类或方法中使用具体的类型信息时。
  3. 等效性与限制

    • 在某些情况下,通配符和类型变量可以提供类似的功能,但类型变量不支持下界限制(例如,不能写T super SomeType),而通配符可以(例如,可以写? super SomeType)。
  4. 使用场景

    • 当你编写一个通用方法,且该方法的逻辑不关心具体类型时,可以使用通配符?来提供适配和限制。
    • 当你需要操作具体的类型或者声明类的类型参数时,应该使用类型变量T
  5. 类型参数与通配符的区别

    • 类型参数(例如,T)定义了一个代表特定作用域内的类型的变量,允许你在类或方法中使用具体的类型。
    • 通配符(?)定义了一组可用于泛型类型的允许类型,它的意思是“在这里可以使用任何类型”,但可以通过上界(? extends SomeType)或下界(? super SomeType)来进一步限制这些类型。

通过这个总结,我们可以更清晰地理解类型变量和通配符在Java泛型编程中的不同角色和应用场景。

例子:

?和 T 都表示不确定的类型,区别在于我们可以对 T 进行操作,但是对 ?不行,比如如下这种 :

   // 可以T t = operate();// 不可以?car = operate();

简单总结下:

T 是一个 确定的 类型,通常用于泛型类和泛型方法的定义,?是一个 不确定 的类型,通常用于泛型方法的调用代码和形参,不能用于定义类和泛型方法。

区别1:通过 T 来 确保 泛型参数的一致性
   // 通过 T 来 确保 泛型参数的一致性public <T extends Number> voidtest(List<T> dest, List<T> src)//通配符是 不确定的,所以这个方法不能保证两个 List 具有相同的元素类型public voidtest(List<? extends Number> dest, List<? extends Number> src)

像下面的代码中,约定的 T 是 Number 的子类才可以,但是申明时是用的 String ,所以就会飘红报错。

不能保证两个 List 具有相同的元素类型的情况

   GlmapperGeneric<String> glmapperGeneric = new GlmapperGeneric<>();List<String> dest = new ArrayList<>();List<Number> src = new ArrayList<>();glmapperGeneric.testNon(dest,src);

上面的代码在编译器并不会报错,但是当进入到 testNon 方法内部操作时(比如赋值),对于 dest 和 src 而言,就还是需要进行类型转换。

区别2:类型参数可以多重限定而通配符不行

img

使用 & 符号设定多重边界(Multi Bounds),指定泛型类型 T 必须是 MultiLimitInterfaceA 和 MultiLimitInterfaceB 的共有子类型,此时变量 t 就具有了所有限定的方法和属性。对于通配符来说,因为它不是一个确定的类型,所以不能进行多重限定。

区别3:通配符可以使用超类限定而类型参数不行

类型参数 T 只具有 一种 类型限定方式:

T extends A

但是通配符 ? 可以进行 两种限定:

? extends A
? super A
区别4.不能实例化

image-20240530224030013

    public <T> List<T> getList() {// ...return new ArrayList<T>();}

需要改成这种

区别5.不能用作泛型类的声明
// 错误的用法,不能这样使用通配符 
public class GenericClass<?> {    // ... 
}
区别6.类型不确定,甚至不能加入最大类型的objectimage-20240530224633303

3、泛型擦除

3.1 概念

编译器编译带类型说明的集合时会去掉类型信息

  • 分析:
    • 这是因为不管为泛型的类型形参传入哪一种类型实参,对于Java来说,它们依然被当成同一类处理,在内存中也只占用一块内存空间。从Java泛型这一概念提出的目的来看,其只是作用于代码编译阶段,在编译过程中,对于正确检验泛型结果后,会将泛型的相关信息擦出,也就是说,成功编译过后的class文件中是不包含任何泛型信息的。泛型信息不会进入到运行时阶段。
    • 在静态方法、静态初始化块或者静态变量的声明和初始化中不允许使用类型形参。由于系统中并不会真正生成泛型类,所以instanceof运算符后不能使用泛型类

Java 语言中引入了泛型以在编译时提供更严格的类型检查并支持泛型编程。为了实现泛型,Java 编译器将类型擦除应用于:

如果类型参数是无界的,则将泛型类型中的所有类型参数替换为其边界或 Object。因此,生成的字节码仅包含普通的类、接口和方法。
必要时插入类型转换以保持类型安全。
生成桥接方法以保留扩展泛型类型中的多态性。
类型擦除确保不会为参数化类型创建新类;因此,泛型不会产生运行时开销。
型形参传入哪一种类型实参,对于Java来说,它们依然被当成同一类处理,在内存中也只占用一块内存空间。从Java泛型这一概念提出的目的来看,其只是作用于代码编译阶段,在编译过程中,对于正确检验泛型结果后,会将泛型的相关信息擦出,也就是说,成功编译过后的class文件中是不包含任何泛型信息的。泛型信息不会进入到运行时阶段。

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

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

相关文章

6个PPT素材模板网站,免费!

免费PPT素材模板下载&#xff0c;就上这6个网站&#xff0c;建议收藏&#xff01; 1、菜鸟图库 ppt模板免费下载|ppt背景图片 - 菜鸟图库 菜鸟图库是一个设计、办公、媒体等素材非常齐全的网站&#xff0c;站内有几百万的素材&#xff0c;其中PPT模板就有几十万个&#xff0c;…

[stm32]——定时器与PWM的LED控制

目录 一、stm32定时器 1、定时器简介 2、定时器分类 3、通用定时器介绍 二、PWM相关介绍 1、工作原理 2、PWM的一般步骤 三、定时器控制LED亮灭 1、工程创建 2、代码编写 3、实现效果 四、采用PWM模式&#xff0c;实现呼吸灯效果 1、工程创建 2、代码编写 3、实现效果 一、stm3…

STM32 IIC协议

本文代码使用 HAL 库。 文章目录 前言一、什么是IIC协议二、IIC信号三、IIC协议的通讯时序1. 写操作2. 读操作 四、上拉电阻作用总结 前言 从这篇文章开始为大家介绍一些通信协议&#xff0c;包括 UART&#xff0c;SPI&#xff0c;IIC等。 UART串口通讯协议 SPI通信协议 一、…

B端系统:角色与权限界面设计,一文读懂。

一、什么是角色与权限系统 角色与权限系统是一种用于管理和控制用户在系统中的访问和操作权限的机制。它通过将用户分配到不同的角色&#xff0c;并为每个角色分配相应的权限&#xff0c;来实现对系统资源的权限控制和管理。 在角色与权限系统中&#xff0c;通常会定义多个角色…

【深度揭秘GPT-4o】:全面解析新一代AI技术的突破与优势

目录 ​编辑 1.版本对比&#xff1a;从GPT-3到GPT-4&#xff0c;再到GPT-4o的飞跃 1.1 模型规模的扩展 1.2 训练数据的更新 1.3 算法优化与效率提升 1.4 案例分析 2.技术能力&#xff1a;GPT-4o的核心优势 2.1 卓越的自然语言理解 2.1.1 上下文理解能力 2.1.2 语义分…

el-table中的信息数据过长 :show-overflow-tooltip=‘true‘**

可以在 el-table-column中添加 :show-overflow-tooltip‘true’

Kotlin 2.0 重磅发布! 性能提升!新功能上线!开发者必看!

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

【Java面试】六、Spring框架相关

文章目录 1、单例Bean不是线程安全的2、AOP3、Spring中事务的实现4、Spring事务失效的场景4.1 情况一&#xff1a;异常被捕获4.2 情况二&#xff1a;抛出检查异常4.3 注解加在非public方法上 5、Bean的生命周期6、Bean的循环引用7、Bean循环引用的解决&#xff1a;Spring三级缓…

软考随记(二)

I/O系统的5种不同的工作方式&#xff1a; 程序控制方式&#xff1a; 无条件查询&#xff1a;I/O端口总是准备好接受主机的输出数据&#xff0c;或是总是准备好向主机输入数据&#xff0c;而CPU在需要时随时直接利用I/O指令访问相应的I/O端口&#xff0c;实现与外设的数据交换 …

python-求点积

【问题描述】&#xff1a;给出两个数组&#xff0c;并求它们的点积。 【问题描述】&#xff1a;输入A[1,1,1],B[2,2,2]&#xff0c;输出6,即1*21*21*26。输入A[3,2],B[2,3,3],输出-1&#xff0c;没有点积。 完整代码如下&#xff1a; alist(map(int,input().split())) blist(…

vscode 默认终端(Terminal) 为CMD,但是新建是powerShell

☆ 问题描述 vscode 默认终端&#xff08;Terminal&#xff09; 为CMD&#xff0c;但是新建是powerShell ★ 解决方案 随便设置其他为默认&#xff0c;然后再设置回来CMD为默认就行了&#xff0c;实在不行就重装vscode吧… ✅ 总结 应该是vscode的小bug

redis windows环境下的部署安装

2024Redis windows安装、部署与环境变量 一、下载 Redis官网目前暂不支持Windows版本&#xff0c;只能从github中下载。 windows 64位系统下载redis路径&#xff1a;https://github.com/tporadowski/redis/releases&#xff0c;下载zip包。 目前Windows版本只更新到5.0的版本…

香橙派OriengePi AiPro 华为昇腾芯片开发板开箱测评

香橙派OriengePi AiPro 华为昇腾芯片开发板开箱测评 文章目录 前言OrangePi AIpro硬件相关及配置外观接口配置虚拟桌面网络配置拓展swap内存 软件相关及配置docker基础镜像搭建pytorch安装及匹配 软件测试使用yolo v8测试使用模型转换 总结 前言 博主有幸受邀CSDN测评香橙派与…

我喜欢的vscode插件

有个更全的&#xff1a;提高编程效率的30个VScode插件 Image preview&#xff08;图片预览&#xff09; any-rule&#xff08;正则表达式大全&#xff09; px to rem & rpx & vw(cssrem)&#xff08;px和rem之间转换&#xff09; 小程序开发助手 Auto Close Tag A…

Java字符串String详解

Java中的String类作为存储和操作文本数据的基本类型&#xff0c;是开发过程中最常用的类型。 String类型的声明及初始化与基本数据类型非常相似&#xff1a; String name "lcy";但是String类型是引用类型&#xff0c;有着非常丰富的处理字符串的方法。正是因为其重…

连锁美容机构行业:如何利用用友BIP收入云实现收入增长

随着消费市场的不断升级和消费者需求的日益多样化&#xff0c;连锁美容机构行业正面临着前所未有的机遇与挑战。在这个竞争激烈的市场环境中&#xff0c;如何有效地利用技术手段提升管理效率、优化客户体验&#xff0c;进而实现收入的持续增长&#xff0c;成为了众多美容机构关…

Javaweb第九次作业

采用XML映射文件的形式来映射sql语句&#xff1b;采用动态sql语句的方式&#xff0c;实现条件查询的分页。 controller Slf4j RestController RequestMapping("supermarket111") public class SupermarketFenyeController {AutowiredSupermarketFenyeService super…

Rust之函数式语言特性:迭代器和闭包(一):概述

开发环境 Windows 11Rust 1.78.0 VS Code 1.89.1 项目工程 这次创建了新的工程minigrep. 函数式语言特性:迭代器和闭包 Rust的设计从许多现有语言和技术中获得了灵感&#xff0c;其中一个重要影响是函数式编程。函数式编程通常包括通过在参数中传递函数、从其他函数返回函数、…

【免费Web系列】JavaWeb实战项目案例五

这是Web第一天的课程大家可以传送过去学习 http://t.csdnimg.cn/K547r 新增员工 前面我们已经实现了员工信息的条件分页查询。 那今天我们要实现的是新增员工的功能实现&#xff0c;页面原型如下&#xff1a; ​ 首先我们先完成"新增员工"的功能开发&#xff0…

[Redis]Hash类型

基本命令 hset命令 设置 hash 中指定的字段&#xff08;field&#xff09;的值&#xff08;value&#xff09; 返回值&#xff1a;添加的字段的个数&#xff08;注意是添加的个数&#xff0c;而不包括修改的&#xff09; hset key field value [field value ...] hget命令 …