幻像类型提高了编译时的安全性

介绍

使用幻像类型是一种非常简单的技术,可用于提高代码的编译时安全性。 有许多潜在的用例具有不同的复杂性级别,但是即使幻像类型的使用非常轻巧,也可以显着提高编译时的安全性。 幻像类型只是带有未使用类型参数的参数化类型。 例如:

public class MyPhantomType<T> {public String sayHello() {return 'hello';}// other methods/fields that never refer to T
}

该示例类的类型参数为T,但实际上从未在代码中使用。 乍一看,这似乎没有什么用,但事实并非如此! 幻像类型的所有对象实例都带有类型信息,因此该技术可用于“标记”带有一些可在编译时检查的额外信息的值。 当然,我们可以在不使用泛型的情况下编写代码来随时逃避键入操作,但是应不惜一切代价避免这样做。 某些语言(例如Scala)完全不允许删除类型参数,因此使用Scala时,您将始终必须完全保留类型信息。

示例用例和实现

幻像类型最简单,最有用的用例之一是数据库ID。 如果我们有一个典型的三层(数据,服务,Web)Java Web应用程序,则可以通过在架构的“端点”以外的所有地方用幻像类型替换原始id来获得很多编译时安全性。 因此,数据层会将原始ID放入数据库查询中,而Web层可能会从外部资源(例如HTTP参数)获取原始ID,但是否则,我们始终会处理幻像类型。 在此示例中,我假设数据库ID类型始终为64位长数字。 首先,我们需要将由所有“实体类”实现的标记器接口,该接口应受幻像类型id机制支持:

public interface Entity {Long getId();
}

这个标记接口的唯一目的是将我们的幻像型id限制为一组标记的类,并提供将在实现中使用的getId方法。 实际的幻像类型是单个id值的不可变容器。 type参数表示id的“目标类型”,这使得可以以编译时安全的方式在不同实体的id值之间进行区分。 我喜欢将此类称为Ref(参考的简写),但这只是个人选择。

@Value
@RequiredArgsConstructor(AccessLevel.PRIVATE)
public final class Ref<T extends Entity> implements Serializable {public final long id;  public static <T extends Entity> Ref<T> of(T value) {return new Ref<T>(value.getId());}public static <T extends Entity> Ref<T> of(long id, Class<T> clazz) {return new Ref<T>(id);}@Overridepublic String toString() {return String.valueOf(id);}}

此示例类使用Project Lombok中的@Value和@RequiredArgsConstructor批注。 如果您不使用Lombok,请手动添加构造函数,getter,equals和hashCode实现(或在下面查找完整的实现)。 注意类型参数T永远不会在任何地方使用。 这也意味着您在运行时无法知道Ref的类型,但这通常不是必需的。

使用示例实现

现在,我们将在可能的情况下将原始ID替换为Refs。 例如,我们可以有一个将用户添加到组中的服务级别方法:

void addUserToGroup(long userId, long groupId);
// without parameter names
void addUserToGroup(long, long);// VSvoid addUserToGroup(Ref<User> userRef, Ref<Group> groupRef);
// without parameter names
void addUserToGroup(Ref<User>, Ref<Group>);

现在,当我们要调用此方法时,将始终需要Ref对象而不是原始的long值。 在此示例中,有两种获取参考值的方法。

  1. 如果您有实际对象的实例,请调用Ref.of(object)。 这是除Web以外的其他层中最常见的方法
  2. 如果您有原始ID,并且知道目标类型,请调用Ref.of(id,TargetType.class)。 如果原始ID来自外部,则通常在Web层中需要这样做

为了从Ref提取原始ID值,您可以阅读该字段或使用getter。 通常仅在构建数据库查询之前才需要这样做。

总结思想

为了了解裁判的好处,请尝试考虑以下情况:

  • 如果您在采用不同类型ID的方法调用中更改参数顺序,会发生什么情况? (例如我们的addUserToGroup)
  • 如果更改数据库ID的类型(例如Integer-> Long或Long-> UUID)会发生什么?
  • 如果您经常具有与id相同类型的方法参数,但它们不是id,那么您将有多大可能出现运行时错误? 例如,如果您有整数ID,并且在同一方法中混合了ID和某种列表索引

在所有这些情况下,使用Refs都可以确保在代码不正确的地方出现编译时错误。 在典型的代码库中,这是不费吹灰之力的巨大胜利。 编译时的安全性降低了重构的成本和难度,这使得维护代码库变得非常容易和安全。

数据库ID只是幻像类型的简单示例。 其他典型的用例包括某种状态机(例如,Order <InProcess>,Order <Completed>与仅Order对象),以及某种类型的值单元信息(例如,LongNumber <Weight>,LongNumber <Temperature>与longs) 。

Ref <T>实现(无Lombok)

public final class Ref<T extends Entity> implements Serializable {public final long id;public static <T extends Entity> Ref<T> of(T value) {return new Ref<T>(value.getId());}public static <T extends Entity> Ref<T> of(long id, Class<T> clazz) {return new Ref<T>(id);}@Overridepublic String toString() {return String.valueOf(id);}private Ref(long id) {this.id = id;}public long getId() {return this.id;}@Overridepublic int hashCode() {return (int) (id ^ (id >>> 32));}@Overridepublic boolean equals(Object o) {if (this == o)return true;if (o == null || o.getClass() != this.getClass())return false;Ref<?> other = (Ref<?>) o;return other.id == this.id;}
}

参考: Gekkio的技术博客博客中的JCG合作伙伴 Joonas Javanainen提出了幻像类型 , 提高了编译时安全性 。

翻译自: https://www.javacodegeeks.com/2013/02/increased-compile-time-safety-with-phantom-types.html

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

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

相关文章

NI Multisim元件库:在Multisim中创建自定义元器件

转载于&#xff1a; http://www.ni.com/tutorial/3173/zhs/ 概览 「在Multisim中创建自定义元器件」与「在 NI Ultiboard中创建自定义元器件」为您提供了关于如何直观、快速地学习如何创建您自己的自定义元器件的信息资源。目录 引言步骤一&#xff1a;输入初始元器件信息步骤二…

字符串的预处理

C isalpha、isalnum、islower、isupper用法 https://blog.csdn.net/weixin_41162823/article/details/80172379 C/C库函数&#xff08;tolower/toupper&#xff09;实现字母的大小写转换 https://blog.csdn.net/laozhuxinlu/article/details/51539737 字符串的逆序 https:…

2017年最新基于Bootstrap 4 的专业、多用途响应式布局的系统模板

本文分享一款2017年最新的2017年最新基于Bootstrap 4 的专业、多用途响应式布局的系统模板&#xff0c;该模板是一款强大并且非常灵活的后台管理系统模板&#xff1a;能适应绝大多数的web应用程序开发&#xff0c;比如&#xff1a;APP的管理后台&#xff0c;电商网站&#xff0…

apache gobblin mysql_gobblin简单使用

一、下载编译解压后查看目录下是否存在gradle/wrapper/gradle-wrapper.jar 文件使用 ./gradlew build -x findbugsMain -x test -x rat -x checkstyleMain编译项目(因网络因素可能有几次失败)编译后文件apache-gobblin-incubating-bin-0.14.0.tar.gz二、配置编写启动命令(需指定…

liunx之Centos6.8杀毒软件的安装

作者:邓聪聪 为了防止服务器中病毒&#xff0c;安装了类似与Windowns的杀毒软件Clanav&#xff0c;过程如下 首先下载clamav的软件包&#xff0c;官方下载地址为http://www.clamav.net/download.html 。我这里是下载好的rpm包链接&#xff1a;https://pan.baidu.com/s/1n052Udk…

上位机串行通讯的通用思路

先上代码&#xff1a; public class SerialPortServer{//字段SerialPort SP new SerialPort();int DelayTime 5000;//默认是5sstring Info "";string SendStringBuffer "";string ReceiveStringBuffer "";//构造函数public SerialPortServe…

您的日志就是您的数据:logstash + elasticsearch

今天的帖子的主题与日常的编码和开发无关&#xff0c;但是涵盖了一个非常重要的主题&#xff1a;我们的应用程序日志文件。 我们的应用程序确实会生成大量日志&#xff0c;如果处理正确&#xff0c;则非常有助于解决问题。 如果您启动并运行一个应用程序并没什么大不了&#xf…

关于C++中二维vector使用

https://blog.csdn.net/u014453443/article/details/98057251

面向对象设计与构造第一次总结作业

第一次作业——多项式计算 ---结构分析 第一次作业我只使用了两个类&#xff0c;正像下面的类图所表示的那样&#xff0c;分别是Poly和ComputePoly。Poly类是不可变的&#xff0c;能保存一个多项式&#xff0c;可以进行加、减运算。ComputePoly是程序的主类&#xff0c;能够读取…

想提升java知识的同学请进

这是我最近在整理的笔记&#xff0c;大家可以看看。 https://www.gitbook.com/book/jackal007/java_more/ 如果觉得有问题可以提出&#xff0c;随时修改。 这个笔记是我花了好多时间看各种书籍&#xff0c;博客后整理的&#xff0c;希望能帮助到更多的朋友。转载于:https://www…

mysql 3种报错_MySQL读取Binlog日志常见的3种错误-阿里云开发者社区

MySQL读取Binlog日志常见的3种错误mysqlbinlog: [ERROR] unknown variable default-character-setutf8mb4当我们在my.cnf中添加default-character-setutf8mb4选项&#xff0c;那么在mysqlbinlog查看binlog时就会报错。解决方案&#xff1a;.mysqlbinlog 后面添加 --no-defaults…

膨胀的JavaBeans –不要在您的API中添加“ Getters”

我已经最近在博客的想法的JavaBeans™如何可以扩展以减少在Java世界中&#xff0c;这被广泛接受的公约设立的膨胀。 该文章在DZone上重新发布&#xff0c;并在这里获得了颇具争议的反馈&#xff08;例如&#xff0c;大多数试图将一些新想法带入Java世界的想法&#xff09;。 我…

css:hover伪类的使用

&#xff1a;hover的使用&#xff0c;即当鼠标指针移入元素时&#xff0c;所做出的样式设置 示例一 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>demo01</title><style>*{margin: 0;p…

Redis内存淘汰机制

转自&#xff1a;https://my.oschina.net/andylucc/blog/741965 摘要 Redis是一款优秀的、开源的内存数据库&#xff0c;我在阅读Redis源码实现的过程中&#xff0c;时时刻刻能感受到Redis作者为更好地使用内存而费尽各种心思&#xff0c;例如最明显的是对于同一种数据结构在不…

python字符串常见操作

字符串常见操作 如有字符串mystr hello world itcast and itcastcpp&#xff0c;以下是常见的操作 <1>find 检测 str 是否包含在 mystr中&#xff0c;如果是返回开始的索引值&#xff0c;否则返回-1 mystr.find(str, start0, endlen(mystr))<2>index 跟find()方法…

mysql数据库的体系结构包括什么组件_MySQL数据库的体系结构

MySQL体系结构遵循C/S模型&#xff0c;即客户机/服务器模型&#xff0c;MySQL客户机通过原生的多种接口&#xff0c;可以与MySQL服务器进行交互。MySQL数据库是单进程多线程的架构&#xff0c;同windows平台下的Oracle数据库与全平台下的达梦数据库一样&#xff0c;采取单进程多…

同步多线程集成测试

测试线程非常困难&#xff0c;这使得为要测试的多线程系统编写良好的集成测试非常困难。 这是因为在JUnit中&#xff0c;测试代码&#xff0c;被测对象和任何线程之间没有内置的同步。 这意味着&#xff0c;当您必须为创建并运行线程的方法编写测试时&#xff0c;通常会出现问题…

调整图像的灰度级数C++实现

图像灰度级数我们见得最多的就是256了&#xff0c;如果想调整它的灰度级数&#xff0c;我们可以使用图像库的imadjust函数来作出调整&#xff0c;比如讲256个灰度级变成2个灰度级&#xff08;也就是二值图了&#xff09;。再举一个例子&#xff0c;原来一幅256个灰度级的图像&a…

angular4获得焦点事件_Angular 4 文本框自动获取焦点二

Angular是不推荐直接通过DOM操作获取元素的&#xff0c;要操作元素就通过ViewChild装饰器。在HTML中对元素添加引用myInput&#xff1a;在ts中可以通过ViewChild获取指定元素的引用&#xff1a;import { ViewChild } from angular/core;ViewChild(myInput) input;获取到对应元素…

Bootstrap源码解读之栅格化篇

本文纯属自己研究所写笔记&#xff0c;如果有错误还请多多指教提出 版心(container) 版心&#xff1a;class名为.container的容器&#xff0c;其版心的宽度在各个屏幕设备下是不一样的值&#xff0c;版心两边就是留白。 各尺寸下版心宽度如下表&#xff1a; 屏幕设备版心宽度ma…