掌握java泛型

news2025/1/1 14:06:28

泛型方法

一般定义如下,即方法的前面加了个<T>

public class FTest {
	public <T> List<T> f(T t){...};
}

三种泛型参数推断方式:

  1. 直接在f()前面加确定泛型
fTest.<Integer>f(xxx)
  1. 通过输入参数确定, 下面这个推断为Integer
int number = 0;
fTest.f(number)
  1. 可通过 返回值 确定
List<Integer> list = fTest.f(xxx);

Q: 下面这段代码哪里有问题? 是toString()那里吗?

public class A<T> {
	public static void  test(T t){
  		System.out.println(t.toString());  
  }
}

A:
test是static方法, 因此无法感知A<T>实例里的T
需要改成
public static <T> void test(T t)

toString()那里没问题,toString就是Object的方法。

泛型参数和类型消除

Q: 泛型参数T在运行时,会变成什么?
A: 统一变成Object且不包含任何类型信息。


Q: 泛型参数T可以可以使用instanceof做比较吗?

class A<T> {
   void f(Object arg)
   if(arg instanceof T) {
	  ...
   }
}

A: 不能,编译器会报错。


Q: 泛型参数T可以进行new T()或者new T[]操作吗?
A: 不能,编译器会报错。


Q: 能调用泛型参数对象里的方法吗?

T.f();

A: 只能调用Object的方法。


Q: 可以用T做强制转化吗?

T t = (T)object;

A: 能运行, 但不会真正发生转型, 编译时会触发waring警告。

新建泛型对象时的问题

先假定有2个类, 基类Parent 和子类Child

class Parent{}
class Child extends Parent{}

回答以下问题:
Q:
下面这句话有问题吗?

List<Parent> list = new ArrayList<Child>()

A:
有问题,编译就错误了。 List<Parent>和ArrayList<Child>并不存在父子类的关系


Q:

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

这个list有什么特点?
A:
这个list可以调用A a = list.get(), 但是不能list.add(new Parent())

  • 原因:
    list.get()所做的操作是在返回时, 把内部的<? extend Parent> 强转成Parent, 是合理的,任何Parent的子类都可以转成Parent
    list.add(new Parent())所做的操作是在输入时, 把外部的A转成内部的<? extend Parent>, 这是不合理的,因为我们不知道这个Parent对象可以转成哪个Parent的子类。

Q:

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

这个list有什么特点?
下面谁会报错

list.add(new Child())
list.add(new Parent())
Parent a= list.get();
Child b = list.get()

A:
截图如下:

image.png

  • Child c = list.get() 或者Parent p = list.get()所做的操作是在返回时, 把内部的<? super Child> 强转成外部的Parent或者child, 是不合理的, 因为编译器觉得child的父类 不一定 能转成parent或者child,所以禁止了这种行为( 比如parent的父类是object, 但object不一定就能转成parent或者child)
    *list.add(new Child())所做的操作是在输入时, 把外部的child或者parent转成内部的<? super Child>, 这是合理的,因为child和parent一定能转成child的父类。

Q:

List<?> list = new ArrayList<A>();

这个list有什么特点?
A:
get和add都不行,只能做remove等无返回值无输入A的操作。
PS: 注意,不是说不能调用get或add方法, 而是调用get或add时,不能使用A这个对象去操作。
即无法做add(A) 或者 A a = get(0)
但是可以做add(object) 或者Object o = get(0)
因为?可以转为Object, 但是无法转为A。


Q:
下面这个代码会报错吗?

   List<Fruit> fruitList = new ArrayList<>();
   fruitList.add(new Fruit());
   List<Apple> appleList = new ArrayList<>();
   appleList.add(new Apple());
   fruitList.addAll(appleList);
   System.out.println(fruitList);

A:
不会报错。会正常打印结果。

image.png


PECS原则
注意PECS原则和上面的区别!
上面之前提到的? extend或者? supert, 都是在声明对象的时候用的。
而PECS原则是用于泛型对象的方法输入参数!

假设有一个类定义如下:

public static class MyList<T> {
    List<T> list = new ArrayList<>();

    // 把输入参数塞给自己,类似于生产操作
    public void pushList(List<T> t) {
        list.addAll(t);
    }

    // 把自己的内容塞给输入参数,类似于让输入参数做消费。
    public void pollList(List<T> t) {
         t.addAll(list);
    }
}

则T就是泛型参数。
Q:下面代码能正常运行吗?

MyList<Number> myList = new MyList<>();

List<Integer> intList = new ArrayList<>();
myList.pushList(intList);

List<Object> objectList = new ArrayList<>();
myList.pollList(objectList);

A:
不能正常运行, pushList和pollList都会报错
因为编译器检查后,认为 List<Integer>和List<Number>不是一个东西!


Q: 如果上文要支持pushList,应该怎么修改pushList方法的定义?
A:
改成这样:

// 把输入参数塞给自己,类似于生产操作
public void pushList(List<? extends T> t) {
    list.addAll(t);
}

即编译器认为,List<Integer> 和List<? extend Number>是一个东西,允许!


Q: 如果要支持pollList,怎么修改定义?
A:

// 把自己的内容塞给输入参数,类似于让输入参数做消费。
public void pollList(List<? super T> t) {
    t.addAll(list);
}

因为是把自己的东西塞给输入参数, 而想要能塞进去,必须保证自己这个T,是输入参数的子类,反过来说,输入参数必须是T的父类,所以用super
于是编译器认为,List<Object> 和List<? super Number>是一个东西,允许!


PECS原则出自Effective Java, 注意只是一个编程建议而已!

  • 如果有一个类A,泛型参数为T
  • 如果他一般只用于接收输入容器List后,塞入自己内部的T容器, 则类A就叫生产者, 因此输入参数最好定义为<? extend T>最好, 以便能接收任何T子类的容器。
  • 如果他一般只用于接收输入容器后List, 把自己内部的T元素塞给它, 那么这个类A就叫消费者, 输入参数最好定义为<? super T>\ 最好, 以便自己的T元素能塞给任何T元素的父类容器。

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

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

相关文章

STM32-外部中断浅析

本篇解释了STM32中断原理 MCU为什么需要中断 中断&#xff0c;是嵌入式系统中很重要的一个功能&#xff0c;在系统运行过程中&#xff0c;当出现需要立刻处理的情况时&#xff0c;暂停当前任务&#xff0c;转而处理紧急任务&#xff0c;处理完毕后&#xff0c;恢复之前的任务…

DPDK源码分析之(1)libmbuf模块

DPDK源码分析之(1)libmbuf模块 Author&#xff1a;OnceDay Date&#xff1a;2024年7月2日 漫漫长路&#xff0c;有人对你笑过嘛… 全系列文档可参考专栏&#xff1a;源码分析_Once-Day的博客-CSDN博客 参考文档&#xff1a; DPDK downloadGetting Started Guide for Linux…

Java面试题--JVM大厂篇之深入解析G1 GC——革新Java垃圾回收机制

目录 引言: 正文&#xff1a; 一、G1 GC的区域划分及其作用 1. 伊甸园区&#xff08;Eden Region&#xff09; 2. 幸存者区&#xff08;Survivor Region&#xff09; 3. 老年代区&#xff08;Old Generation Region&#xff09; 二、区域划分的优势: 三、图片解析: 结…

记录自己Ubuntu加Nvidia驱动从入门到入土的一天

前言 记录一下自己这波澜壮阔的一天&#xff0c;遇到了很多问题&#xff0c;解决了很多问题&#xff0c;但是还有很多问题&#xff0c;终于在晚上的零点彻底放弃&#xff0c;重启windows。 安装乌班图 1.安装虚拟机 我开始什么操作系统的基础都没有&#xff0c;网上随便搜了…

代码随想录算法训练营第四十九天| 300.最长递增子序列 , 674. 最长连续递增序列 , 718. 最长重复子数组

300. 最长递增子序列 - 力扣&#xff08;LeetCode&#xff09; class Solution {public int lengthOfLIS(int[] nums) {int[] dp new int[nums.length];dp[0] 1;for(int i1;i<nums.length;i){for(int j0;j<i;j){if(nums[i] > nums[j]){dp[i] Math.max(dp[j],dp[i])…

(十) Docker compose 本地部署 apollo

文章目录 1、apollo2、数据库准备3、启动后会用到的几个地址4、docker-compose运行 apollo方式一&#xff1a;使用容器 hostName 作为网络媒介方式二&#xff1a;使用端口映射固定 ip 作为网络媒介 6、客户端 1、apollo https://www.apolloconfig.com/#/zh/deployment/quick-s…

微软Edge浏览器全解析:从速度到安全性的全面体验

微软Edge浏览器&#xff0c;自2015年首次亮相以来&#xff0c;已经成为了浏览器市场上不可忽视的一股力量。它不仅集成了Windows 10的许多原生功能&#xff0c;还在速度和安全性上进行了大量的优化。本文将全面解析微软Edge浏览器的各项特性&#xff0c;带您领略这款浏览器的魅…

Linux笔记之三

Linux笔记之三 一、用户组管理二、磁盘管理三、进程管理总结 一、用户组管理 每个用户都有一个用户组&#xff0c;系统可以对一个用户组中的所有用户进行集中管理&#xff08;开发、测试、运维、root&#xff09;。不同Linux系统对用户组的管理涉及用户组的添加、删除和修改。…

基于Java+SpringMvc+Vue技术的图书管理系统的设计与实现(60页论文参考)

博主介绍&#xff1a;硕士研究生&#xff0c;专注于Java技术领域开发与管理&#xff0c;以及毕业项目实战✌ 从事基于java BS架构、CS架构、c/c 编程工作近16年&#xff0c;拥有近12年的管理工作经验&#xff0c;拥有较丰富的技术架构思想、较扎实的技术功底和资深的项目管理经…

防火墙概述

1、防火墙 防火墙顾名思义就是防止火灾发生时&#xff0c;火势烧到其它区域&#xff0c;使用由防火材料砌的墙。在网络安全中&#xff0c;防火墙的作用就是保护本地网络不受到外部网络或恶意程序的伤害。 防火墙的核心任务是控制和防护&#xff0c;即通过安全策略识别流量并做…

Kafka抛弃Zookeeper后如何启动?

Kafaka如何下载 官网地址 目前Kafka最新的版本就是3.7.1 我们可以看到下面这两个版本信息&#xff1f;什么意思呢&#xff1f; Scala 2.12 - kafka_2.12-3.7.1.tgz (asc, sha512)Scala 2.13 - kafka_2.13-3.7.1.tgz (asc, sha512) 我们应该知道&#xff0c;一个完整的Kafka实…

【Excel】把一列姓名快速填充到多列表格

目录标题 1. 输入A1 → 向右填充 → 输入A5 → 向右填充2. 选中2行单元格 → 向下填充3. CtrlH → 替换 → 全部替换 1. 输入A1 → 向右填充 → 输入A5 → 向右填充 2. 选中2行单元格 → 向下填充 3. CtrlH → 替换 → 全部替换

用Goaccess对Web及雷池WAF日志实现可视化分析

君衍. 一、项目环境介绍二、Goaccess1、Goaccess介绍2、存储方式3、配置选项4、自定义日志/日期格式5、特殊格式说明符 三、雷池访问日志1、配置文件改变2、docker配置3、示例测试 四、Goaccess安装1、安装依赖2、编译安装 五、Goaccess对Nginx日志分析1、常用命令参数2、终端模…

【server】springboot 整合 redis

1、redis 使用模式 1.1 单机模式 1.1.1 编译安装方式 1.1.1.1 下载 Redis的安装非常简单&#xff0c;到Redis的官网&#xff08;Downloads - Redis&#xff09;&#xff0c;下载对应的版本&#xff0c;简单几个命令安装即可。 1.1.1.2 编译安装 tar xzf redis-stable.tar.…

【链表】【双指针】1、环形链表+2、环形链表 II+3、相交链表

2道简单1道中等&#xff0c;链表的双指针相关问题就告一段落&#xff0c;下一步刷新的题目。 1、环形链表&#xff08;难度&#xff1a;简单&#xff09; 该题对应力扣网址 AC代码 常见思路&#xff0c;slow指针每次走一步&#xff0c;fast指针每次走两步&#xff0c;如果fa…

大语言模型基础

大语言基础 GPT : Improving Language Understanding by Generative Pre-Training 提出背景 从原始文本中有效学习的能力对于减轻自然语言处理中对监督学习的依赖至关重要。很多深度学习方法需要大量人工标注的数据&#xff0c;限制了它们在很多领域的应用&#xff0c;收集更…

Learn To Rank

在信息检索中&#xff0c;给定一个query&#xff0c;搜索引擎召回一系列相关的Documents&#xff0c;然后对这些Documents进行排序&#xff0c;最后将Top N的Documents输出。 排序问题最关注的是各Documents之间的相对顺序关系&#xff0c;而不是各个Documents的预测分最准确。…

如何优化 PostgreSQL 中对于自关联表的查询?

文章目录 一、理解自关联表查询二、分析性能问题的可能原因&#xff08;一&#xff09;缺少合适的索引&#xff08;二&#xff09;大量数据的笛卡尔积&#xff08;三&#xff09;复杂的查询逻辑 三、优化策略及解决方案&#xff08;一&#xff09;创建合适的索引&#xff08;二…

Rejetto HFS 服务器存在严重漏洞受到攻击

AhnLab 报告称 &#xff0c;黑客正在针对旧版本的 Rejetto HTTP 文件服务器 (HFS) 注入恶意软件和加密货币挖矿程序。 然而&#xff0c;由于存在错误&#xff0c; Rejetto 警告用户不要使用 2.3 至 2.4 版本。 2.3m 版本在个人、小型团队、教育机构和测试网络文件共享的开发…

7.pwn 工具安装和使用

关闭保护的方法 pie: -no-pie Canary:-fno-stack-protector aslr:查看:cat /proc/sys/kernel/randomize_va_space 2表示打开 关闭:echo 0>/proc/sys/kernel/randomize_va_space NX:-z execstack gdb使用以及插件安装 是GNU软件系统中的标准调试工具&#xff0c;此外GD…