Effective Java笔记(30)优先考虑泛型方法

        正如类可以从泛型中受益一般 ,方法也一样。静态工具方法尤其适合于泛型化 。 Collections 中的所有“算法”方法(例如 binarySearch 和 sort )都泛型化了 。

        编写泛型方法与编写泛型类型相类似 。 例如下面这个方法,它返回两个集合的联合 :

public static Set union(Set s1, Set s2) {Set result = new HashSet(s1);result.addAll(s2);return result;
}

        这个方法可以编译,但是有两条警告 :

        为了修正这些警告,使方法变成是类型安全的,要将方法声明修改为声明一个类型参数( type parameter ),表示这三个集合的元素类型(两个参数和一个返回值),并在方法中使用类型参数。 声明类型参数的类型参数列表,处在方法的修饰符及其返 回值之间 。 在这个示例中,类型参数列表为< E >,返回类型为 Set<E > 。 类型参数的命名惯例与泛型方法以及泛型的相同: 

public static <E> Set<E> union(Set<E> s1, Set<E> s2) {Set<E> result = new HashSet<>(s1);result.addAll(s2);return result;
}

        至少对于简单的泛型方法而言,就是这么回事了 。 现在该方法编译时不会产生任何警告,并提供了类型安全性,也更容易使用 。 以下是一个执行该方法的简单程序 。 程序中不包含转换,编译时不会有错误或者警告 :

public static void main(String[] args) {Set<String> guys = Set.of("Tom", "Dick", "Harry");Set<String> stooges = Set.of("Larry","Moe", "Curly");Set<String> af1Cio = union(guys, stooges); .System.out.println(af1Cio);
}

        运行这段程序时,会打印出[ Moe, Harry , Tom , Curly , Larry , Dick ] 。 (元素的输出顺序是独立于实现的 。 )

        union 方法的局限性在于三个集合的类型(两个输入参数和一个返回值)必须完全相同 。利用有限制的通配符类型( bounded wildcard type )可以使方法变得更加灵活 。

        有时可能需要创建一个不可变但又适用于许多不同类型的对象 。由于泛型是通过擦除实现的,可以给所有必要的类型参数使用单个对象,但是需要编写一个静态工厂方法,让它重复地给每个必要的类型参数分发对象 。 这种模式称作泛型羊例工厂(generic singleton factory ),常用于函数对象,如 Collections.reverse Order ,有时也用于像 Collections.emptySet 这样的集合 。

        假设要编写一个恒等函数( identity function)分发器。 类库中提供了 Function.identity,因此不需要自己编写,但是自己编写也很有意义 。 如果在每次需要的时候都重新创建一个 ,这样会很浪费,因为它是无状态的( stateless ) 。 如果 Java 泛型被具体化了,每个类型都需要一个恒等函数,但是它们被擦除后,就只需要一个泛型单例 。 请看以下示例 :

private static UnaryOperator<Object> IDENTITY_FN = (t) -> t;@SuppressWarnings ("unchecked")
public static <T> UnaryOperator<T> identi tyFunction() {return (UnaryOperator<T>) IDENTITY_FN;
}

        IDENTITY_FN 转换成( UnaryFunction<T>),产生了一条未受检的转换警告,因为UnaryFunction<Object >对于每个 T 来说并非都是个 UnaryFunction<T > 。 但是恒等函数很特殊 : 它返回未被修改的参数,因此我们知道无论 T 的值是什么,用它作为 Unary ­Function<T >都是类型安全的 。 因此,我们可以放心地禁止由这个转换所产生的未受检转换警告 。 一旦禁止,代码在编译时就不会出现任何错误或者警告 。

        下面是一个范例程序,它利用泛型单例作为 UnaryFunction<String >和 Unary­Function<Number > 。 像往常一样,它不包含转换,编译时没有出现错误或者警告 :

public static void main(String[] args) {String[] strings = { "jute", "hemp", "nylon" };UnaryOperator<String> sameString = identityFunction();for (String s : strings)System.out.printIn(sameString.apply(s));Number[] numbers = { 1,2.0, 3L };UnaryOperator <Number> sameNumber = identityFunction();for (Number n : numbers)System.out.println(sameNumber . apply(n)) ;
}

        虽然相对少见,但是通过某个包含该类型参数本身的表达式来限制类型参数是允许的 。这就是递归类型限制( recursive type bound ) 。 递归类型限制最普遍的用途与 Comparable接口有关,它定义类型的自然顺序 。 这个接口的内容如下 :

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

        类型参数 T 定义的类型,可以与实现 Cornparable<T >的类型的元素进行比较 。 实际上,几乎所有的类型都只能与它们自身的类型的元素相比较 。 例如 String实现 Comparable<String>, Integer 实现 Comparable< Integer>,等等 。

        有许多方法都带有一个实现 Comparable 接口的元素列表,为了对列表进行排序,并在其中进行搜索,计算出它的最小值或者最大值,等等 。 要完成这其中的任何一项操作,都要求列表中的每个元素能够与列表中的每个其他元素相比较,换句话说,列表的元素可以互相比较( mutually comparable ) 。 下面是如何表达这种约束条件的一个示例 :

public static <E extends Comparable<E>> E max(Col1ection<E> C) ;

        类型限制< E extends Cornparable<E>>,可以读作“针对可以与自身进行比较的每个类型 E ”,这与互比性的概念或多或少有些一致 。

        下面的方法就带有上述声明 。 它根据元素的自然顺序计算列表的最大值,编译时没有出现错误或者警告:

pub1ic static <E extends Comparable<E>> E max(Collection<E> c) {if(c.isEmpty())throw new IllegalArgumentException("Empty collection");E result = null;for(E e:c)if (result == null || e.compareTo(result) > 0)result = Objects.requireNonNull(e);return result;
}

        注意,如果列表为空,这个方法就会抛出 IllegalArgumentException 异常 。 更好的替代做法是返回一个 Optional<E>  。

        递归类型 限制可能比这个要复杂得多 ,但幸运的是,这种情况并不经常发生 。 如果你理解了这种习惯用法和它的通配符变量,以及模拟自类 型( simulated selιtype )习惯用法 , 就能够处理在实践中遇到的许多递归类型限制了 。

        总而言之,泛型方法就像泛型一样,使用起来比要求客户端转换输入参数并返回值的方法来得更加安全,也更加容易 。 就像类型一样 ,你应该确保方法不用转换就能使用 ,这通常意味着要将它们泛型化 。 并且就像类型一样,还应该将现有的方法泛型化,使新用户使用起来更加轻松 ,且不会破坏现有 的客户端。

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

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

相关文章

iOS问题记录 - Xcode 15安装低版本iOS模拟器(持续更新)

文章目录 前言开发环境问题描述问题分析1. 定位问题2. 逆向分析2.1. IDA Free2.2. Hopper Disassembler Demo 3. 模拟器日志4. supportedArchs 解决方案最后 前言 最近新需求很多&#xff0c;项目改动很大&#xff0c;开发完成后想测一遍在低版本iOS系统上的兼容性&#xff0c…

Elasticsearch Boolean Query查询介绍

前言 ES 和 Solr 的底层都是基于Apache Lucene 实现&#xff0c;bool 查询的底层实现是Lucene 的 BooleanQuery&#xff0c;其可以组合多个子句查询&#xff0c;类似 SQL 语句里面的 OR 查询。 查询介绍 在 ES 里面 Boolean 查询封装了 4 种 API 接口能力&#xff0c;可以单独…

分享21年电赛F题-智能送药小车-做题记录以及经验分享

这里写目录标题 前言一、赛题分析1、车型选择2、巡线1、OpenMv循迹2、灰度循迹 3、装载药品4、识别数字5、LED指示6、双车通信7、转向方案1、开环转向2、位置环速度环闭环串级转向3、MPU6050转向 二、调试经验分享1、循迹2、识别数字3、转向4、双车通信5、逻辑处理6、心态问题 …

Docker卸载安装及国内镜像源(详细版)

文章目录 一、卸载已有Docker1、首先判断本地有没有docker&#xff1a;2、判断CentOS下 docker是否在运行&#xff1a;3、停止docker运行&查看状态4、yum查看docker安装的包并卸载5、删除docker安装目录6、查看docker version 二、Docker安装及镜像源配置1、centOS 7 yum源…

Jay17 2023.8.10日报

笔记 【python反序列化】 序列化 类对象->字节流&#xff08;字符串&#xff09; 反序列化 字节流->对象 python反序列化没PHP这么灵活&#xff0c;没这么多魔术方法。 import pickle import os class ctfshow(): def init(self): self.username0 self.password0 d…

【数理知识】求刚体旋转矩阵和平移矩阵,已知 N>=3 个点在前后时刻的坐标,且这 N>=3 点间距离始终不变代表一个刚体

序号内容1【数理知识】自由度 degree of freedom 及自由度的计算方法2【数理知识】刚体 rigid body 及刚体的运动3【数理知识】刚体基本运动&#xff0c;平动&#xff0c;转动4【数理知识】向量数乘&#xff0c;内积&#xff0c;外积&#xff0c;matlab代码实现5【数理知识】最…

第五十天

●软件测试的目的 软件测试的目的是寻找错误&#xff0c;并且尽可能找出更多的错误。 测试是程序的执行过程&#xff0c;目的在于发现错误 一个好的测试用例在于能够发现至今为止未发现的错误 一个成功的测试是发现了至今未发现的错误的测试 ●软件测试工作流程&#xff1…

轻松预约,尽享美食,详解餐厅预约小程序的设计与实现

随着智能手机的普及和人们生活水平的提高&#xff0c;餐厅预约已经成为人们日常生活中的一部分。为了更好地满足人们的需求&#xff0c;许多餐厅开始使用小程序来提供更方便快捷的预约服务。本文将介绍如何制作一款餐厅预约小程序的详细步骤。 1. 进入乔拓云网后台&#xff0c;…

uni-app微信小程序开发自定义select下拉多选内容篇

分享-2023年资深前端进阶&#xff1a;前端登顶之巅-最全面的前端知识点梳理总结 *分享一个使用比较久的&#x1fa9c; 技术框架公司的选型&#xff1a;uni-app uni-ui vue3 vite4 ts 需求分析&#xff1a;微信小程序-uni-ui内容 1、创建一个自定义的下拉&#xff0c;支持多…

OSPF无法建立领居的原因有哪些(第三十五课)

1 配置OSPF 1.1 思路 1&#xff0c;配置IP地址 2&#xff0c;配置OSPF 配置进程号 route-id进入区域宣告网段 配置IP地址 R1路由表 ------------------------------------------------------------------------------ Routing Tables: Public Destinations : 10 …

《人脸识别技术应用安全管理规定(征求意见稿)》,需要关注三个焦点

目录 严防人脸信息采集与滥用 规范人脸识别信息的处理 保障人脸识别技术的安全 人脸识别主要有三类风险 近日&#xff0c;国家互联网信息办公室发布《人脸识别技术应用安全管理规定&#xff08;试行&#xff09;&#xff08;征求意见稿&#xff09;》公开征求意见的通知。 …

Python 模块 locust 性能测试

简介 locust 是 Python 的一个开源的负载测试工具&#xff0c;用于测试网络应用程序的性能和可伸缩性。它使用Python编写&#xff0c;并提供了一个简单易用的语法来定义和执行负载测试。locust模块允许用户模拟大量并发用户并观察系统在高负载下的响应情况。 目录 1. 基本用法…

多线程的实现方式Thread、Runnable、Callable

1.并发和并行 并发&#xff1a;在同一时刻&#xff0c;有多个指令在单个CPU上交替执行。 并行&#xff1a;在同一时刻&#xff0c;有多个指令在多个CPU上同时执行 2.多线程的实现方式 2.1 继承Thread类实现方式 2.2 实现Runnable接口的实现方式 2.3 利用Callable接口和Futur…

【网站收录前端需要怎么做?】

网站收录前端需要怎么做 什么是网站收录网站收录失败原因1. 网站被搜索引擎拉黑2. 网站结构不合理3. 网站内容质量差4. 网站没有被索引5. 网站新建时间太短 前端如何帮助提高网站收录 什么是网站收录 要想知道前端怎么做&#xff0c;首先我们来先了解一下这个收录是什么&#…

学习Helm来提高K8S运维效率

阶段一&#xff1a;了解 Helm 的基础知识和概念 Helm 简介&#xff1a; Helm 是 Kubernetes 的一个包管理工具&#xff0c;用于简化应用程序在 Kubernetes 上的部署和管理过程。它允许您使用预定义的模板&#xff08;Charts&#xff09;来描述应用程序的组件、依赖关系和配置…

基于金融行业的软件测试分析

随着银行业务不断增加&#xff0c;业务模式不断复杂化&#xff0c;对我们的银行软件也要求越来越高&#xff0c;产出高质量的产品也非常重要&#xff0c;下面对银行软件测试进行分析总结。 银行软件集中度高&#xff0c;规模庞大&#xff0c;往往是以系统群的方式存在&#xff…

F. Sum and Product - 思维

分析&#xff1a; 题目中的格式有点像韦达定理&#xff0c;就是对于一元二次方程ax^2 bx c 0有 所以可以推出要找的就是两个点&#xff0c;可以直接二分查找存不存在&#xff0c;这题有很多边界问题&#xff0c;有b^2 - 4ac小于0或者等于0&#xff0c;或者求出来的根在数组中…

【STM32】利用CubeMX对FreeRTOS用按键控制任务

对于FreeRTOS中的操作&#xff0c;最常用的就是创建、删除、暂停和恢复任务。 此次实验目标&#xff1a; 1.创建任务一&#xff1a;LED1每间隔1秒闪烁一次&#xff0c;并通过串口打印 2.创建任务二&#xff1a;LED2每间隔0.5秒闪烁一次&#xff0c;并通过串口打印 3.创建任…

创建型设计模式:5、原型模式(Prototype Pattern)

目录 1、原型模式的含义 2、C实现原型模式的简单实例 1、原型模式的含义 通过复制现有对象来创建新对象&#xff0c;而无需依赖于显式的构造函数或工厂方法&#xff0c;同时又能保证性能。 The prototype pattern is a creational design pattern in software development. …