【再探】Java—泛型

news2024/9/20 20:39:55

 Java 泛型本质是参数化类型,可以用在类、接口和方法的创建中。

1 “擦除式”泛型

Java的“擦除式”的泛型实现一直受到开发者的诟病。

“擦除式”的实现几乎只需要在Javac编译器上做出改进即可,不要改动字节码、虚拟机,也保证了以前没有使用泛型的库可以之间运行在java 5.0 之上。但是这个带来了以下弊端:

  1. 例如对于List<String> 和 List<Integer> ,在运行时由于擦除了,所以这两个都变成了List,因此它们在运行中是同一类型。(而对于C#的泛型来说,无论在源码中、编译后及运行时它们始终是不同的类型)。这导致在运行时,获取不到类型信息。
  2. “擦除式”的实现,是在元素被赋值时编译器自动插入类型检查指令,访问元素时,自动插入类型强制转换指令。这样频繁的类型检查及转换,导致Java的泛型性能差于C#的泛型。
public class EraseGeneric {

    private static class Holder<T> {
        T t;

        public T getT() {
            return t;
        }

        public void setT(T t) {
            this.t = t;
        }
    }

    public static void main(String[] args) {
        Holder<String> holder = new Holder<>();
        holder.setT("hello");
        String str = holder.getT();
    }

}

图 Holder类被编译后的字节码片段

图 main 方法中,对于Holder类型的赋值及访问操作字节码

1.1 擦除的补偿

如果要在运行时获取类型信息,那么可以通过引入类型标签来对擦除进行补偿。

public class TypeTagGeneric {

    private static class User {
        public User() {
        }
    }

    private static <T> void fun(Class<T> kind) throws InstantiationException, IllegalAccessException {
        T t = kind.newInstance();
        System.out.println(t);
    }

    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        fun(User.class);
        int[] array1 = new int[10];
        String[] array2 = new String[10];
    }

}

2 协变与逆变

A 类型是B的父类型,对于某个构造器,构造出的复杂类型A`与B`。

协变

A`仍然是B`的父类型。比如Java中的数组,A[] 仍是B[]的父类型。

逆变

B`是A`的父类型。

抗变

A`与B`没有任何继承关系。例如List<A> 与List<B>没有任何继承关系。

表 协变、逆变与抗变

2.1 数组与泛型

T[] t = new T[10]; 这个代码是错误的,Java中规定不能创建泛型数组。

因为Java 在运行时,无法获取泛型的类型信息,因为在创建数组时,也就无法获取到泛型参数所表示的确切类型。

2.1.1 数组的类型

Java中数组的种类有两种:

  1. 基础类型的数组:[ + 开头大写字母。

int[] : [I

  1. 引用类型的数组:[ + L + 类型。

String[] array2 : [Ljava/lang/String

public class ArrayGeneric {

    private static class Fruit {}

    private static class Apple extends Fruit {}

    public static void main(String[] args) {
        Fruit[] fruits = new Fruit[10];
        Apple[] apples = new Apple[10];
        System.out.println(fruits instanceof Fruit[]); // true
        System.out.println(fruits instanceof Apple[]); // false
        System.out.println(apples instanceof Fruit[]); // true
        System.out.println(apples instanceof Apple[]); // true
        fruits = apples;
//        apples = fruits; // 编译错误
        System.out.println(fruits.getClass().getSuperclass()); // class java.lang.Object
        System.out.println(apples.getClass().getSuperclass()); // class java.lang.Object
//        getSuperclass() 方法:如果此 Class 表示 Object 类、一个接口、一个基本类型或 void,则返回 null。
//        如果此对象表示一个数组类,则返回表示该 Object 类的 Class 对象。否则返回该类的超类。
    }

}

2.2 通配符

泛型中的通配符用于在两个类型之间建立某种类型的向上转型关系。

协变

? extends T, 例如List<? extends Fruit> list。确定了元素类型的父类为Fruit,但不能确定其确切类型,因此不能往该容器添加新的元素(只能添加null)。但是可以从容器中提取元素,类型为Fruit。

逆变

? super T,例如List<? super Apple> list,确定了元素为Apple的父类,因为可以往容器中添加元素,但不能提取元素。

表 通配符的协变与逆变

public class CovarianceAndContravariance {

    private static class Fruit {}

    private static class Apple extends Fruit {}

    private static <T extends Apple> void setItem(List<? super Apple> list, T item) {
        list.add(item);
    }

    private static Fruit getItem(List<? extends Fruit> list,int pos) {
        return list.get(pos);
    }

    public static void main(String[] args) {
        List<? super Apple> list = new ArrayList<>();
        setItem(list,new Apple());
        List<? extends Fruit> list2 = Arrays.asList(new Fruit(),new Apple());
        Fruit item = getItem(list2, 0);
    }

}

2.2.1 无界通配符

无界通配符,例如List<?>, 其相当于List<? extends Object>,但不等价于List(相当于List<Object>)。其有两个作用:

  1. 告诉编译器,我用了泛型,只是还没确定哪个类型;
  2. 用于捕获类型。
public class CaptureGeneric {

    private static class Holder<T> {}

    private static <T> void fun1(Holder<T> holder) {
        System.out.println(holder);
    }

    private static void fun2(Holder<?> holder) {
        fun1(holder);
    }

    public static void main(String[] args) {
        Holder holder = new Holder(); //  原生类型
        fun1(holder); // 警告,Unchecked assignment:
        fun2(holder); // 不会警告,无边界通配符将不会这个原生类型的类型参数(Object)
    }

}

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

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

相关文章

MVVM_MVVMLight架构

介绍 MVVMLight是一个实现MVVM模式的轻量级框架&#xff08;相对于Prism&#xff09;&#xff0c;能够更好的帮助我们开发WPF Windows Phone、Windows 8、SilverLight相关项目。 安装 在NuGet 工具箱上搜索MVVMLight进行下载 下载后它帮你生成ViewModel文件夹&#xff0c;里面…

深度学习之基于Tensorflow卷积神经网络脑肿瘤分类识别系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景 脑肿瘤是医学领域的一个重要问题&#xff0c;对人类的健康构成了严重威胁。传统的脑肿瘤分类识别方法…

老的 IIS + MSSQL 网站迁移实例

因为公司需要从云上迁移回本地&#xff0c;但云平台不愿意导出虚拟机文件&#xff0c;所以公司需要手工迁移。 查看了一下云主机&#xff0c;安装了IIS&#xff0c;还有MSSQL数据库&#xff0c;于是在本地搭建好相同的OSIISMSSQL 环境&#xff0c;在把数据库导入完成、 IIS 搭建…

AcWing 217:绿豆蛙的归宿 ← 搜索算法

【题目来源】https://www.acwing.com/problem/content/219/【题目描述】 给出一个有向无环的连通图&#xff0c;起点为 1&#xff0c;终点为 N&#xff0c;每条边都有一个长度。 数据保证从起点出发能够到达图中所有的点&#xff0c;图中所有的点也都能够到达终点。 绿豆蛙从起…

iptables防火墙【☆】

一、防火墙的基础 防火墙分为硬件防火墙和软件防火墙&#xff0c;硬件防火墙有专门的设备&#xff1a;如国产华为、绿盟等&#xff0c;作为网关层做地址转换等安全防护。很多公司基本都会使用硬件防火墙做第一道防御&#xff0c;在核心业务上再添加软件防火墙提高安全性能…

Tailwind CSS快速入门

文章目录 初识安装Tailwindcss试用安装快速书写技巧扩展好处Todo 初识 只需书写 HTML 代码&#xff0c;无需书写 CSS&#xff0c;即可快速构建美观的网站 Tailwind CSS 是一个功能类优先的 CSS 框架&#xff0c;它通过提供大量的原子类&#xff08;utility classes&#xff09;…

国产操作系统上使用SQLynx连接数据库 _ 统信 _ 麒麟 _ 中科方德

原文链接&#xff1a;国产操作系统上使用SQLynx连接数据库 | 统信 | 麒麟 | 中科方德 Hello&#xff0c;大家好啊&#xff01;今天我们将探讨如何在国产操作系统上使用SQLynx。这是一款功能强大的数据库管理工具&#xff0c;可以帮助用户高效地管理和操作数据库。本文将详细介绍…

《网关微服务技术架构:构建高效可扩展的服务网关》

随着微服务架构的流行&#xff0c;网关微服务作为微服务架构中的重要组成部分&#xff0c;扮演着连接客户端与后端微服务的关键角色。本文将深入探讨网关微服务的技术架构设计与实现&#xff0c;以及如何构建高效可扩展的服务网关。 ### 1. 网关微服务的作用与意义 网关微服务…

【Power Compiler手册】2.Power Compiler设计流程

当创建设计时,它从高层次的抽象转移到门级最终实现。功耗编译器工具在整个设计周期中,从RTL到门级,提供分析和优化。 有关设计流程的信息,请参见以下主题: • 设计周期中的功耗 • 功耗优化和分析流程 设计周期中的功耗 在每个抽象层次上,使用仿真、分析和优化来完善…

GMSL图像采集卡,适用于无人车、自动驾驶、自主机器、数据采集等场景,支持定制

基于各种 系列二代 G MS L 图像采集卡&#xff08;以下简称 二代图像采集卡&#xff09;是一款自主研发的一款基于 F P G A 的高速图像产品&#xff0c;二代图像采集卡相比一代卡&#xff0c;由于采用PCIe G en 3 技术&#xff0c;速度和带宽都相应的有了成 倍的提高。该图像…

递归的例子

例1&#xff1a;阶乘函数 #include<iostream> using namespace std; int f(int n) {if(n0)return 1;elsereturn f(n-1)*n; } int main() {int n;cin>>n;cout<<f(n);return 0; }例2&#xff1a;Fibonacci数列 无穷数列1&#xff0c;1&#xff0c;2&#xff0…

微服务:eureka的搭建,以及服务注册、服务发现、负载均衡

eureka 搭建 新建一个Module,maven项目&#xff0c;导入依赖。 <dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId></dependency><…

Day 59 503.下一个更大元素Ⅱ 42.接雨水

下一个更大元素Ⅱ 给定一个循环数组&#xff08;最后一个元素的下一个元素是数组的第一个元素&#xff09;&#xff0c;输出每个元素的下一个更大元素。数字 x 的下一个更大的元素是按数组遍历顺序&#xff0c;这个数字之后的第一个比它更大的数&#xff0c;这意味着你应该循环…

JVM学习-动态链接和方法返回地址

动态链接–指向运行时常量池的方法引用 每一个栈帧内部包含一个指向运行时常量池中该栈帧所属方法的引用&#xff0c;包含这个引用的目的为了支持当前方法的代码能够实现动态链接(Dynamic Linking)&#xff0c;如invokednamic指令。在Java源文件被编译到字节码文件中时&#x…

【字典树(前缀树) 位运算】1803. 统计异或值在范围内的数对有多少

本文涉及知识点 字典树&#xff08;前缀树&#xff09; 位运算 LeetCode1803. 统计异或值在范围内的数对有多少 给你一个整数数组 nums &#xff08;下标 从 0 开始 计数&#xff09;以及两个整数&#xff1a;low 和 high &#xff0c;请返回 漂亮数对 的数目。 漂亮数对 是…

运维出现的问题 --集成

运维出现的问题 集成 macos 本地打的镜像&#xff0c;推到线上出现 images platform (linux/arm64) does not match the detected解决办法 macos 本地打的镜像&#xff0c;推到线上出现 image’s platform (linux/arm64) does not match the detected WARNING: The requested …

抖音视频怎么去水印保存部分源码|短视频爬虫提取收集下载工具

抖音视频怎么去水印保存部分源码|短视频爬虫提取收集下载工具 抖音视频去水印保存部分源码&#xff1a; 通过使用Python中的requests、re和os等库&#xff0c;可以编写如下代码来实现抖音视频去水印保存的功能。 短视频爬虫提取手机下载工具的使用方法&#xff1a; 该工具主…

项目集成SkyWalking,基于k8s搭建

一、搭建SkyWalking 官方文档&#xff08;英文&#xff09;&#xff1a;skywalking/docs at master apache/skywalking 中文可以使用&#xff1a;GitHub - SkyAPM/document-cn-translation-of-skywalking: [已过期,请使用官网AI文档] The CN translation version of Apache…

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-19讲 串口实验UART

前言&#xff1a; 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM&#xff08;MX6U&#xff09;裸机篇”视频的学习笔记&#xff0c;在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…

跟着Kimi学习结构化提示词:19套内置提示词都在这里了!

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;所以创建了“AI信息Gap”这个公众号&#xff0c;专注于分享AI全维度知识…