什么是 Java 泛型?怎样使用 Java 泛型?

news2024/11/17 4:29:21

目录

1、为什么使用泛型?

2、什么是泛型类?如何定义一个泛型类?

泛型的命名约定

3、什么是泛型方法?如何定义一个泛型方法?

4、什么是有界类型参数?如何定义有界类型参数?

(1)多个边界的类型参数定义

(2)有界类型参数在泛型方法中的应用

5、如何区别泛型类和它的子类型?


        所谓泛型,即参数化类型,目的是将具体类型参数化,在使用时需要传入具体类型进行替换。参数又分为实参和形参,泛型属于类型形参(好比抽象函数,是一种泛指,类似于数学函数中用 x,y,z 代表具体的值)。

1、为什么使用泛型?

        泛型可以使类型在定义类、接口和方法的时侯成为参数,与方法中使用的形参非常相似,类型参数(泛型)提供了一种通过不同的输入而重用代码的方式。类型参数和形参的不同之处在于形参输入的是具体的值,而类型参数输入的是类型。

        使用泛型可以在编译时检测到更多的错误,从而增加代码的稳定性。

        使用泛型可以带来以下好处:

        (1)在编译时拥有更强的类型检查。Java 编译器会对泛型进行强类型检查,并在代码违反类型安全的时后产生错误信息。一般来说,修复编译时错误比修复运行时的错误会更加容易,因为运行时错误往往更难以找到。

        (2)消除类型之间的强制转换。例如,以下代码没有使用泛型就需要进行类型转换:

List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0);

        当把以上代码改成使用泛型时,则不再需要强制进行转换:

List<String> list = new ArrayList<String>();
list.add("hello");
String s = list.get(0);   // no cast

        (3)使程序员能够实现泛型算法。通过使用泛型,程序员可以实现适用于不同类型集合的泛型算法,这些算法可以自定义,并且类型安全,代码也更易于阅读。// 参考一些集合类的定义

2、什么是泛型类?如何定义一个泛型类?

        泛型类是指通过对参数类型进行泛化的类或接口。下面的 Box.class 可以演示这个概念。首先,如果  Box.class 在不使用泛型的情况下是这样定义的:

public class Box {
    private Object object;

    public void set(Object object) { this.object = object; }
    public Object get() { return object; }
}

        该方法接受或返回 Object,所以除了基本的数据类型外,可以传入任何类型的对象。但是,当上边程序在进行编译时,没有办法去验证这个类是如何使用的。比如,一开始可能会往 Box 中放置一个Integer 对象,而接下来有可能会错误地传入一个String 对象,如果此时仍然期望从 Box 中获得一个 Integer 对象,会导致运行时错误。

        泛型类的定义格式如下:

class name<T1, T2, ..., Tn> { /* ... */ }

        类型参数部分,用尖括号(<>)进行分隔,并跟在类名后面。它指定的类型参数为 T1,T2,…,和 Tn

        如果要使用泛型更新 Box 类,可以通过将代码 “public class Box” 更改为 “public class Box<T>” 来创建泛型类声明。该方式引入了类型变量 T,该变量可以在类中的任何地方使用。

/**
 * Generic version of the Box class.
 * @param <T> the type of the value being boxed
 */
public class Box<T> {
    // T stands for "Type"
    private T t;

    public void set(T t) { this.t = t; }
    public T get() { return t; }
}

        如你所见,所有出现的 Object 都被 T 取代,类型变量 T 可以是任何类型。同样的技术也可以应用于创建通用接口。下边是对于接口的声明示例:

泛型的命名约定

        按照约定,泛型的名称是单个的大写字母。这与变量的命名约定形成了鲜明的对比,如果没有这些约定,就很难区分类型变量和普通类或接口命名之间的区别。

        最常用的泛型名称如下:

  • E - 代表元素(被Java集合框架广泛使用)
  • K - 代表键 Key
  • N - 代表数字类型
  • T - Type(代表类型)
  • V - 代表值 Value
  • S,U,V etc. - 代表第二个,第三个,第四个类型

调用和实例化泛型

        在 Java SE 7 及更高版本中,只要编译器能够从上下文确定或推断类型参数,就可以将调用构造函数所需的类型参数替换为类型参数的空集(<>)。例如,可以使用以下代码创建 Box<Integer> 的实例:// 类型推断,是Java编译器根据方法调用或方法声明来确定参数类型的能力。

Box<Integer> integerBox = new Box<>();

3、什么是泛型方法?如何定义一个泛型方法?

        泛型方法是引入了类型参数的方法。类似于声明泛型类,但类型参数的作用域仅限于声明它的方法。Java 允许使用静态的和非静态的泛型方法,以及泛型构造函数。

        泛型方法的声明包括一个类型参数集 <K,V>,该类型参数集位于尖括号内,出现在方法的返回类型之前对于静态泛型方法,强制要求类型参数必须出现在方法的返回类型之前

        Util 类包含一个泛型方法 compare,用于比较两个 Pair 对象:

public class Util {
    // 静态泛型方法
    public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) {
        return p1.getKey().equals(p2.getKey()) &&
               p1.getValue().equals(p2.getValue());
    }
}

public class Pair<K, V> {

    private K key;
    private V value;
    // 泛型构造函数
    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }
    // 泛型方法
    public void setKey(K key) { this.key = key; }
    public void setValue(V value) { this.value = value; }
    public K getKey()   { return key; }
    public V getValue() { return value; }
}

        调用此方法的完整代码如下:

Pair<Integer, String> p1 = new Pair<>(1, "apple");
Pair<Integer, String> p2 = new Pair<>(2, "pear");
// boolean same = Util.<Integer, String>compare(p1, p2);
boolean same = Util.compare(p1, p2);

        一般来说,调用方法中的这个类型 <Integer, String> 可以省略,Java 编译器会推断出正确的类型。通过类型推断,允许将泛型方法作为普通方法调用,而不需要在尖括号内指定具体的类型。

4、什么是有界类型参数?如何定义有界类型参数?

        有时候,可能想严格限制泛型的定义界限,例如,操作数字的方法只能接受 Number 或 Number 子类的实例,这时候就需要用到有界类型参数。

        要声明一个有界类型参数,需要列出类型参数的名称,跟上 extends 关键字,然后再跟上它的上界(本例中是 Number)。// extends 限制了类型参数的上界

public class Box<T> {

    private T t;          

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

    public T get() {
        return t;
    }

    public <U extends Number> void inspect(U u){
        System.out.println("T: " + t.getClass().getName());
        System.out.println("U: " + u.getClass().getName());
    }

    public static void main(String[] args) {
        Box<Integer> integerBox = new Box<Integer>();
        integerBox.set(new Integer(10));
        integerBox.inspect("some text"); // error: this is still String!
    }
}

        除了可以限制泛型的实例化范围外,有界类型参数还允许实例调用边界类型中定义的方法

public class NaturalNumber<T extends Integer> {

    private T n;

    public NaturalNumber(T n)  { this.n = n; }

    public boolean isEven() {
        return n.intValue() % 2 == 0;
    }

    // ...
}

        在上述示例的 isEven() 方法中,n 可以调用定义在 Integer 类中的 intValue() 方法。

(1)多个边界的类型参数定义

        上边的例子都是一个类型参数使用一个单一的边界,但是,Java 中一个类型参数可以有多个边界:

<T extends B1 & B2 & B3>

        具有多个边界的类型变量是边界中列出的所有类型的子类型。如果其中一个边界定义的是类,那么必须首先指定它。例如:

Class A { /* ... */ }
interface B { /* ... */ }
interface C { /* ... */ }

class D <T extends A & B & C> { /* ... */ }

        如果未首先指定继承 A.calss,那么会产生编译时错误:

class D <T extends B & A & C> { /* ... */ }  // 编译错误

(2)有界类型参数在泛型方法中的应用

        有界类型参数是实现泛型算法的关键。例如下面的方法,它计算数组 T[] 中大于指定元素 elem 的元素数量。

public static <T> int countGreaterThan(T[] anArray, T elem) {
    int count = 0;
    for (T e : anArray)
        if (e > elem)  // 编译错误
            ++count;
    return count;
}

        这个方法的实现虽然很简单,却不能通过编译,因为大于运算符 (>) 只能应用于基本数据类型,比如:short、int、double、long、float、byte 和 char。所以不能使用 > 操作符来比较一个对象。为了解决这个问题,可以尝试使用 Comparable<T> 接口进行替换:// 借助 Comparable 中的方法对对象进行比较

public interface Comparable<T> {
    public int compareTo(T o);
}

        更改后的代码如下,借助了有界类型参数,使用了边界类中定义的方法:

public static <T extends Comparable<T>> int countGreaterThan(T[] anArray, T elem) {
    int count = 0;
    for (T e : anArray)
        if (e.compareTo(elem) > 0)
            ++count;
    return count;
}

5、如何区别泛型类和它的子类型?

        一般来说,只要类型兼容,就可以将一种类型的对象赋值给另一种类型的对象。例如,可以将一个 Integer 赋值给 Object,因为 Object 是 Integer 的超类型之一。对于泛型也是如此,比如调用一个泛型类,将 Number 作为它的类型参数,如果参数与 Number 兼容,则允许调用 add() 方法:

Box<Number> box = new Box<Number>();
box.add(new Integer(10));   // OK
box.add(new Double(10.1));  // OK

        现在,有如下方法,那么该方法可以接受什么类型的参数呢?

public void boxTest(Box<Number> n) { /* ... */ }

        通过查看它的签名,可以知道它接受一个类型为  Box<Number> 的参数。这又意味着什么呢?该方法是否允许传入 Box<Integer> 或 Box<Double>?最终的答案是 “否”,因为 Box<Integer> 和 Box<Double> 并不是 Box<Number> 的子类型。

       任意给定两种具体类型 A 和 B,无论 A 和 B 之间是否有关系,MyClass<A> 和 MyClass<B> 都没有任何关系。因为 MyClass<A> 和 MyClass<B> 的共同父类是 Object。

        所以,正确的做法是通过继承或实现泛型类或接口,然后对其进行子类型的划分。一个类或接口的类型参数与另一个类或接口的类型参数之间的关系由 extends 和 implements 决定。

        以 Collections 类为例,ArrayList<E> 实现了 List<E>, List<E> 扩展了 Collection<E>。所以ArrayList<String> 是 List<String> 的子类型,而 List<String> 是 Collection<String> 的子类型。所以只要不改变类型参数,类型之间的子类型关系就会保留。

        至此,对于 Java 泛型的基本知识介绍完毕。

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

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

相关文章

Maven高级-私服

分模块合作开发 9.2)Nexus Nexus是Sonatype公司的一款maven私服产品 下载地址&#xff1a;https://help.sonatype.com/repomanager3/download Nexus*安装、启动与配置** 启动服务器&#xff08;命令行启动&#xff09; nexus.exe /run nexus访问服务器&#xff08;默认端口…

linux安装部署vsftpd

yum直接安装yum -y install vsftpd ftp 创建新用户&#xff1a;ftpd更新ftpd密码&#xff1a;echo "123456" |passwd --stdin ftpd创建ftp目录&#xff1a;mkdir -p /home/ftpd/test授权&#xff1a;chown -R ftpd:ftpd /home/ftpd/testchmod 777 -R /home/ftpd/test…

某程序员哀叹:最近阳的人越来越多,面对员工们纷纷倒下,公司领导公然宣称“发烧请病假不等于在家睡大觉,再不回复工作就滚蛋”...

最近阳的人越来越多&#xff0c;面对员工们纷纷倒下&#xff0c;有的公司通情达理&#xff0c;有的公司却开始“不当人”了。一位网友曝光公司领导在群里所有人&#xff0c;称“发烧请病假不意味着在家睡大觉&#xff0c;啥也不管&#xff0c;联系不上&#xff0c;安排不予响应…

【SAP Hana】SAP HANA SQL 基础教程

SAP HANA SQL 基础教程1、SQL 标准简介2、HANA STUDIO 的安装3、HANA STUDIO 的设置4、HANA SQL 基础教程&#xff08;1&#xff09;查看表数据&#xff08;2&#xff09;查看表结构&#xff08;3&#xff09;SELECT&#xff08;4&#xff09;WHERE&#xff08;5&#xff09;WH…

B站直播带货,带货直播数据如何查看?

随着时代发展&#xff0c;直播电商带货也是越来越火&#xff0c;在这个直播带货火热期&#xff0c;B站也是当仁不让的加入到直播带货行业中&#xff0c;在今年双11中&#xff0c;B站第一次参加双十一直播电商混战&#xff0c;但是并未像其他电商平台一般&#xff0c;趁双十一流…

【自学Python】Python浮点型(float)

Python浮点型(float) Python浮点型(float)教程 Python 浮点型数值用于保存带小数点的数值。Python 的浮点数有两种表示形式&#xff0c;即十进制形式和科学计数法形式。 Python浮点型(float)详解 十进制形式 Python 最常见的浮点型数就是十进制形式的浮点型数。Python 中的…

Java-类加载

静态加载和动态加载 4种加载时机&#xff0c;只有反射是动态加载 静态加载举个例子 Cat父类Animal mao是Cat类独有方法 Animal anew Cat(); a.mao();//编译看左边 //左边类型为Animal&#xff08;会加载Animal类&#xff0c;编译时进行加载叫静态加载&#xff09; //然后加载…

OpenShift 容器平台企业版 OCP 4.11.9 部署(基于KVM,CentOS)

参考&#xff1a; 阿里云上Openshift-4.10.5搭建 OpenShift4.8在oVirt下的自动化安装 红帽OpenShift安装部署-阿里云帮助中心 安装配置操作节点&#xff08;Operator&#xff09;&#xff0c;并获取OCP离线安装文件 OCP安装定制文件准备_frank0521的博客-CSDN博客 第 23 章…

【Java数据结构与算法】第二十一章 元组

【Java数据结构与算法】第二十一章 元组 文章目录【Java数据结构与算法】第二十一章 元组1.概念2.自定义元组3.第三方Jar包1.概念 元组&#xff08;Tuple&#xff09;是一种数据结构&#xff0c;可以存放多个元素&#xff0c;每个元素的数据类型可以不同。用List与Tuple类比&a…

深入了解Netty,这一篇就够了

一、Netty简介 Netty是由JBOSS提供的一个java开源框架&#xff0c;现为 Github上的独立项目。Netty提供异步的、事件驱动的网络应用程序框架和工具&#xff0c;用以快速开发高性能、高可靠性的网络服务器和客户端程序。 也就是说&#xff0c;Netty 是一个基于NIO的客户、服务器…

微分方程(人口预测+传染病模型)

一、定义 微分方程&#xff1a;含导数或微分的方程 微分方程的阶数&#xff1a;所含导数或微分的最高阶数&#xff0c;如y’’’2y’’-2x0是三阶微分方程 微分方程的解&#xff1a;使得微分方程成立的函数 例如y’-2x0的解可以为x或者x1 微分方程的通解和特解&#xff1a;特…

【pat】分而治之【图】

分而治之&#xff0c;各个击破是兵家常用的策略之一。在战争中&#xff0c;我们希望首先攻下敌方的部分城市&#xff0c;使其剩余的城市变成孤立无援&#xff0c;然后再分头各个击破。为此参谋部提供了若干打击方案。本题就请你编写程序&#xff0c;判断每个方案的可行性。输入…

MySQL触发器相关知识

1、什么是触发器 触发器&#xff08;trigger&#xff09;是mysql的数据库对象之一&#xff0c;是一种与表操作有关的数据库对象&#xff0c;当触发器所在表上出现指定事件时&#xff08;这些事件包括insert、update、delete三种&#xff09;&#xff0c;将调用该对象&#xff0…

2023年安装Flutter开发环境_在C盘空间占用空间

2023年安装Flutter开发环境&#xff0c;C盘空间还剩多少&#xff1f; 1&#xff1a;Flutter开发对磁盘空间的要求 2&#xff1a;其余日常辅助软件安装D盘&#xff08;占用8GB&#xff09; 3&#xff1a;消耗时间&#xff08;3天–网络有时会中断&#xff09;–【劝退提示】 安…

Hudi(12):Hudi集成Flink之sql-client方式

目录 0. 相关文章链接 1. 启动sql-client 1.1. 修改flink-conf.yaml配置 1.2. local模式 1.3. yarn-session模式 2. 插入数据 3. 查询数据 4. 更新数据 5. 流式插入 5.1. 创建测试表 5.2. 执行插入 5.3. 查看job 5.4. 查看job 5.5. 查看HDFS目录 5.6. 查询结果 …

行为型模式 - 解释器模式Interpreter

学习而来&#xff0c;代码是自己敲的。也有些自己的理解在里边&#xff0c;有问题希望大家指出。 模式的定义与特点 解释器模式&#xff08;Interperter Pattern&#xff09;&#xff0c;给定一个语言&#xff0c;定义它的文法表示&#xff0c;并定义一个解释器&#xff0c;这个…

智引未来,深兰科技机器人家族首次亮相TechG

12月31日&#xff0c;首届上海国际消费电子技术展(简称TechG)在南京国际博览中心圆满落下帷幕。作为全球消费电子技术领域的顶级行业盛会&#xff0c;本届展会共吸引了来自全球的300余家企业出席&#xff0c;共计逾2万名专业人士到场参观。阿里巴巴、蚂蚁科技、海尔、科大讯飞、…

PyQt6快速入门-菜单与工具栏

菜单与工具栏 接下来我们将了解一些常见的用户界面元素,您可能在许多其他应用程序中都见过它们——工具栏和菜单。 我们还将介绍Qt 提供的用于最小化不同 UI 区域之间的重复的整洁系统 — QAction。 1、Toolbars 最常见的用户界面元素之一是工具栏。 工具栏是用于在应用程序…

【微服务】Nacos 账号权限体系

目录 一、背景 1、账号体系 2、账号实体映射 二、方案 1、Nacos 资源模型 2、Nacos 授权 resource 2.1、授权 resource 组成 2.2、不同级别授权资源组成 3、Nacos 授权 Opers 4、Nacos 具体权限定义 4.1、Opers 组成 4.2、具体实例 4.3、工程实现 三、RBAC 设计实…

IDEA使用Spring initializr 创建SpringBoot项目超时问题解决办法

1.问题描述 IDEA使用Spring initializr 创建SpringBoot项目时经常会出现连接超时的问题&#xff0c;报错提示如下 还有一个提示非常简短就是 connect timed out 总之问题都是一样&#xff0c;可能因为是外网所以有时候会出现连接问题&#xff0c;多试几次会成功&#xff0c;…