Java Comparable接口的陷阱

Java Comparable接口提供了一种对实现该接口的类进行自然排序的方法。 自然顺序对标量和其他非常简单的对象有意义,但是当我们使用面向业务的领域对象时,自然顺序就变得更加复杂。 从业务经理的角度来看,交易对象的自然顺序可以是交易的价值,但是从系统管理员的角度来看,自然顺序可以是交易的速度。 在大多数情况下,业务域对象没有明确的自然顺序。

假设我们已经为诸如Company这样的类找到了良好的自然排序。 我们将使用公司的正式名称作为主要订单字段,并使用公司ID作为次要字段。 公司类的实现可以如下。

public class Company implements Comparable<Company> {private final String id;private final String officialName;public Company(final String id, final String officialName) {this.id = id;this.officialName = officialName;}public String getId() {return id;}public String getOfficialName() {return officialName;}@Overridepublic int hashCode() {HashCodeBuilder builder = new HashCodeBuilder(17, 29);builder.append(this.getId());builder.append(this.getOfficialName());return builder.toHashCode();}@Overridepublic boolean equals(final Object obj) {if (obj == this) {return true;}if (!(obj instanceof Company)) {return false;}Company other = (Company) obj;EqualsBuilder builder = new EqualsBuilder();builder.append(this.getId(), other.getId());builder.append(this.getOfficialName(), other.getOfficialName());return builder.isEquals();}@Overridepublic int compareTo(final Company obj) {CompareToBuilder builder = new CompareToBuilder();builder.append(this.getOfficialName(), obj.getOfficialName());builder.append(this.getId(), obj.getId());return builder.toComparison();}
}

该实现看起来不错并且可以正常工作。 对于某些用例,Company类是不够的,因此我们将其扩展到CompanyDetails类,该类提供有关公司的更多信息。 例如,可以在显示公司详细信息的数据表中使用这些类的实例。

public class CompanyDetails extends Company {private final String marketingName;private final Double marketValue;public CompanyDetails(final String id, final String officialName, final String marketingName, final Double marketValue) {super(id, officialName);this.marketingName = marketingName;this.marketValue = marketValue;}public String getMarketingName() {return marketingName;}public Double getMarketValue() {return marketValue;}@Overridepublic int hashCode() {HashCodeBuilder builder = new HashCodeBuilder(19, 31);builder.appendSuper(super.hashCode());builder.append(this.getMarketingName());return builder.toHashCode();}@Overridepublic boolean equals(final Object obj) {if (obj == this) {return true;}if (!(obj instanceof CompanyDetails)) {return false;}CompanyDetails other = (CompanyDetails) obj;EqualsBuilder builder = new EqualsBuilder();builder.appendSuper(super.equals(obj));builder.append(this.getMarketingName(), other.getMarketingName());builder.append(this.getMarketValue(), other.getMarketValue());return builder.isEquals();}
}

乍一看,该实现看起来还不错,但实际上并非如此。 我们可以创建一个小的测试用例来指示实现问题。 当我们不知道类的实际接口在做什么,并且我们对扩展的超类的所有细节没有给予足够的关注时,就会出现问题。

CompanyDetails c1 = new CompanyDetails("231412", "McDonalds Ltd", "McDonalds food factory", 120000.00);
CompanyDetails c2 = new CompanyDetails("231412", "McDonalds Ltd", "McDonalds restaurants", 60000.00);Set<CompanyDetails> set1 = CompaniesFactory.createCompanies1();
set1.add(c1);
set1.add(c2);Set<CompanyDetails> set2 = CompaniesFactory.createCompanies2();
set2.add(c1);
set2.add(c2);Assert.assertEquals(set1.size(), set2.size());

我们使用两个集合,但要意识到它们的行为有所不同。 这是为什么? 另一个集是HashSet,它依赖于对象的hashCode()equals()方法,而另一个是TreeSet,并且仅依赖于Comparable接口,而我们并未为子类实现该接口。 扩展域对象时,这是一个很常见的错误,但更重要的是,这与错误的编码约定有关。 我们使用Apache Commons的构建器来实现hashCode()equals()compareTo()方法。 建设者提供了appendSuper()方法,该方法指示应将其用于该方法的超类的实现。 如果您读过Joshua Bloch撰写的精彩著作《 Effective Java》 ,您将意识到这是不对的。 如果在子类中添加字段,则在不违反对称规则的情况下,我们无法正确实现equals()compareTo()方法。 我们应该使用组合而不是继承。 如果我们使用组合来创建CompanyDetails,那么Comparable接口就不会有问题,因为我们不会自动实现它,并且默认情况下允许行为异常。 而且我们也可以适当地满足equals()hashCode()的要求。

这篇文章中提到的问题很普遍,但通常被忽略。 可比接口的问题实际上是由不正确的约定引起的,并且不了解所用接口的要求。 作为Java开发人员或架构师,您应该注意诸如此类的事情,并遵守良好的编码约定和实践。 项目越大,避免人为因素造成的错误就越重要。 我试图为可比接口总结一个良好的最佳实践列表,以便可以避免错误。

Java可比接口设计和用法的最佳实践:

  • 了解您正在创建的域对象,如果该对象没有明确的自然顺序,则不要实现Comparable接口。
  • 比Comparable更喜欢Comparator实现。 比较器可以根据用例以更面向业务的方式使用。
  • 如果您需要创建依赖于比较对象的接口或库,请尽可能提供自己的Comparator实现,否则创建良好的文档,说明如何为您的接口实现Comparator。
  • 遵守良好的编码约定和惯例。 有效的Java是入门的好书。

参考:来自RAINBOW WORLDS博客的JCG合作伙伴 Tapio Rautonen 的Java Comparable接口的陷阱 。

翻译自: https://www.javacodegeeks.com/2013/08/pitfalls-of-java-comparable-interface.html

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

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

相关文章

mysql 创建视图

CREATE VIEW v_image_org_user_album AS--创建视图并命名v_image_org_user_album SELECT --先开from中表的命名 a.id AS imgid,--将a表中的id命名为新的表&#xff08;视图&#xff09;中的imgid字段&#xff0c;剩下的都一样&#xff0c;就是将之前的字段重新命名到…

jQuery.ajaxPrefilter()函数的使用

jQuery.ajaxPrefilter( [dataTypes ], handler(options, originalOptions, jqXHR) )返回: undefined 描述: 在每个请求之前被发送和 $.ajax()处理它们前处理&#xff0c;设置自定义Ajax选项或修改现有选项。 添加的版本: 1.5jQuery.ajaxPrefilter( [dataTypes ], handler(optio…

linux php自动执行_linux下实现定时执行php脚本

在linux中输入命令复制代码 代码如下:crontab -e然后使用vim的命令编辑打开的文件&#xff0c;输入复制代码 代码如下:0 * * * * /usr/bin/php -f /home/userxxx/update.php保存&#xff0c;退出&#xff0c;好了&#xff0c;现在系统会在每个0点自动执行update.php脚本&#x…

Nginx安装及配置详解

nginx概述 nginx是一款自由的、开源的、高性能的HTTP服务器和反向代理服务器&#xff1b;同时也是一个IMAP、POP3、SMTP代理服务器&#xff1b;nginx可以作为一个HTTP服务器进行网站的发布处理&#xff0c;另外nginx可以作为反向代理进行负载均衡的实现。 这里主要通过三个方面…

Gradle:我们需要另一个构建工具吗?

在Java开发的早期&#xff0c;我们要么没有太多的构建工具需求&#xff0c;要么就使用了其他环境中的工具。 我仍然记得构建shell脚本并创建用于开发Java的文件。 生成文件特别有趣&#xff0c;因为这是一个在设计时就没有考虑Java的工具。 迁移到不同的操作系统环境也很困难。…

servlet简单概括总结

最近在看java web的相关内容&#xff0c;不管是整体还是细节&#xff0c;要学习的知识有很多&#xff0c;所以有一个好的学习体系非常重要。在阅读学习一些博客和教程中关于servlet的内容后&#xff0c;现将知识体系和自己的总结体会进行梳理&#xff0c;希望在更深入理解的同时…

jquery学习之-查找父元素方法parent() parents() closest()的区别

parent()、parents()与closest()方法两两之间有类似又有不同&#xff0c;本篇简短的区分一下这三个方法。通过本篇内容&#xff0c;大家将会在以后使用.parent()、parents()和closest()时不会显得无从下手。 我们直接看例子来来说明一下这三个方法的使用区别&#xff1a; <u…

二叉树的递归遍历与复制

1 #include <iostream>2 3 //定义树的数据结构4 struct BiTNode5 {6 int data;7 struct BiTNode *lchild, *rchild;8 };9 10 typedef struct BiTNode BiTNode;11 typedef struct BiTNode* BiTree;12 13 14 //前序遍历15 void preOrder(BiTNode *root)16 {17…

php超强后门在任意位置创建文件,php大马:.user.ini文件构成的超强PHP后门

这个估计很多同学看了不屑&#xff0c;认为是烂大街的东西了&#xff1a;那么我来个新的吧&#xff1a;。它比用的更广&#xff0c;不管是nginx/apache/IIS&#xff0c;只要是以fastcgi运行的php都可以用这个方法。我的nginx服务器全部是fpm/fastcgi&#xff0c;我的IIS php5.3…

CSS3-字体渐变色

示例&#xff1a;Maugers Blog <!DOCTYPE HTML> <html> <head><meta charset"utf-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><title>Maugers Blog</title><style type"text/css…

Restlet框架– Hello World示例

Restlet是用于Java平台的轻量级&#xff0c;全面的开源REST框架。 Restlet适用于服务器和客户端Web应用程序。 它支持主要的Internet传输&#xff0c;数据格式和服务描述标准&#xff0c;例如HTTP和HTTPS&#xff0c;SMTP&#xff0c;XML&#xff0c;JSON&#xff0c;Atom和WAD…

BZOJ2809 dispatching 【可并堆】

题目分析&#xff1a; yy一下就知道了&#xff0c;合并用可并堆少个log。 代码&#xff1a; 1 #include<bits/stdc.h>2 using namespace std;3 4 const int maxn 102000;5 6 int n,m;7 int b[maxn],c[maxn],l[maxn],sz[maxn];8 long long tot[maxn];9 int dis[maxn],v…

php ci 框架 扩展缓存类,CodeIgniter扩展核心类实例详解

本文实例讲述了CodeIgniter扩展核心类的方法。分享给大家供大家参考&#xff0c;具体如下&#xff1a;CI中对核心类、辅助类和函数的扩展是相当方便的&#xff0c;配置文件中指定了subclass_prefix扩展前缀&#xff0c;默认为MY_&#xff0c;扩展时需要以该配置为前缀&#xff…

jQuery下如何使用Json传递数据

json的结构为&#xff1a; {\"data\":[{\"key1\":\"1\"},{\"key2\":\"2\"},{\"key3\":\"3\"}]} 因为要进行输出&#xff0c;所以里边加了转义符&#xff0c;希望大家不要在此迷惑。下边&#xff0c;我就…

七牛云

待续....转载于:https://www.cnblogs.com/tovictory/p/8647883.html

Servlet 3的异步Servlet功能

在深入了解什么是异步Servlet之前&#xff0c;让我们尝试了解为什么需要它。 假设我们有一个Servlet&#xff0c;处理时间很长&#xff0c;如下所示。 LongRunningServlet.java package com.journaldev.servlet;import java.io.IOException; import java.io.PrintWriter;impo…

简单却实用的的例子:Jquery获取 radio 选中后的文字

今天在 QQ 群里有朋友问到 jQuery 怎么获取选中 radio 后的文本&#xff0c;可见要熟练的使用 jQuery 应该很好的掌握 jQuery 选择器&#xff0c;下边就让我们简单看看这个问题。 首先&#xff0c;他给出的 HTML 示例如下&#xff1a; <input type"radio" id"…

excel的宏与VBA入门——代码调试

直接介绍重点&#xff1a; 常用的操作是导航栏的逐句与断点&#xff1a; 添加断点&#xff1a;调试->切换断点 单步运行&#xff1a;调试->逐句 查看变量的窗口&#xff1a;视图->本地窗口 转载于:https://www.cnblogs.com/jiangbei/p/9561352.html

php访问服务器文件路径,PHP与服务器文件系统的简单交互

1、php.ini中关于文件上传的设置指令2、文件上传过程(1)上传文件提交表单html代码&#xff1a;Adminstration - upoload new filesUpload new filesUpload a file(2)php处理上传文件代码①在php脚本中&#xff0c;需要处理的数据保存在超级变量数组$_FILES中&#xff0c;开启re…

泽西岛客户:测试外部呼叫

吉姆和我在上周一直在做一些工作&#xff0c;其中涉及调用neo4j的HA状态URI来检查实例是否是主/从属&#xff0c;并且我们一直在使用jersey-client 。 该代码大致如下所示&#xff1a; class Neo4jInstance {private Client httpClient;private URI hostname;public Neo4jInst…