转载自:http://my.oschina.net/jack230230/blog/56339
Java的Comparable接口提供一个对实现了这个接口的对象列表进行排序的办法。原始的排序对于简单的对象来说具有意义,但是当我们面对复杂的面向对象的业务逻辑对象时,事情变得复杂的多。从业务经理的角度来看,一些交易对象的自然顺序可能是按照交易的价值来排序的,但是从系统管理员的角度来看,这个排序的规则可能是交易的速度。所以在大多数情况下,并没有明确的业务领域对象的自然排序规则。
假设我们找到了一个需要排序的类,比如说Campany。我们把公司的offical name作为主关键字,把id作为次要关键字。这个类的实现如下:
01 |
public class Company implements Comparable<Company>
{ |
03 |
private final String
id; |
04 |
private final String
officialName; |
06 |
public Company(final String
id, final String
officialName) { |
08 |
this.officialName
= officialName; |
11 |
public String
getId() { |
15 |
public String
getOfficialName() { |
20 |
public int hashCode()
{ |
21 |
HashCodeBuilder
builder = new HashCodeBuilder(17, 29); |
22 |
builder.append(this.getId()); |
23 |
builder.append(this.getOfficialName()); |
24 |
return builder.toHashCode(); |
28 |
public boolean equals(final Object
obj) { |
32 |
if (!(obj instanceof Company))
{ |
35 |
Company
other = (Company) obj; |
36 |
EqualsBuilder
builder = new EqualsBuilder(); |
37 |
builder.append(this.getId(),
other.getId()); |
38 |
builder.append(this.getOfficialName(),
other.getOfficialName()); |
39 |
return builder.isEquals(); |
43 |
public int compareTo(final Company
obj) { |
44 |
CompareToBuilder
builder = new CompareToBuilder(); |
45 |
builder.append(this.getOfficialName(),
obj.getOfficialName()); |
46 |
builder.append(this.getId(),
obj.getId()); |
47 |
return builder.toComparison(); |
这个实现看起来没问题,假设现在这个类提供的信息不够使用,我们又创建了这个类的一个子类CompanyDetail类用以扩展他。例如我们想以一个表的形式显示公司的信息,我们就可以用这个类。
01 |
public class CompanyDetails extends Company
{ |
03 |
private final String
marketingName; |
04 |
private final Double
marketValue; |
06 |
public CompanyDetails(final String
id, final String
officialName, final String
marketingName, final Double
marketValue) { |
07 |
super(id,
officialName); |
08 |
this.marketingName
= marketingName; |
09 |
this.marketValue
= marketValue; |
12 |
public String
getMarketingName() { |
16 |
public Double
getMarketValue() { |
21 |
public int hashCode()
{ |
22 |
HashCodeBuilder
builder = new HashCodeBuilder(19, 31); |
23 |
builder.appendSuper(super.hashCode()); |
24 |
builder.append(this.getMarketingName()); |
25 |
return builder.toHashCode(); |
29 |
public boolean equals(final Object
obj) { |
33 |
if (!(obj instanceof CompanyDetails))
{ |
36 |
CompanyDetails
other = (CompanyDetails) obj; |
37 |
EqualsBuilder
builder = new EqualsBuilder(); |
38 |
builder.appendSuper(super.equals(obj)); |
39 |
builder.append(this.getMarketingName(),
other.getMarketingName()); |
40 |
builder.append(this.getMarketValue(),
other.getMarketValue()); |
41 |
return builder.isEquals(); |
这个类的实现看起来还是没什么问题,但是事实上是有问题的,我们可以写一个test指出问题在哪里。当我们没有对父类的所有细节加以注意时,问题就来了。
01 |
CompanyDetails
c1 = new CompanyDetails("231412", "McDonalds
Ltd", "McDonalds
food factory", 120000.00); |
02 |
CompanyDetails
c2 = new CompanyDetails("231412", "McDonalds
Ltd", "McDonalds
restaurants", 60000.00); |
04 |
Set<CompanyDetails>
set1 = CompaniesFactory.createCompanies1(); |
08 |
Set<CompanyDetails>
set2 = CompaniesFactory.createCompanies2(); |
12 |
Assert.assertEquals(set1.size(),
set2.size()); |
我们构造了两个set,但是结果是assert的结果是不相等。这是为什么?其中一个set是一个HashSet,他依赖对象的hashCode()和equals()方法,但是另一个是TreeSet,他只是依赖Comparable接口,而这个接口在子类中我们并没有实现。在领域对象被扩展的时候这是很常见的一个错误,但是更重要的是这是不好的编码约定造成的。我们使用Apache
Commons包中的builder来实现hashCode(),equals().和compareTo()方法。这些builder提供了appendSuper()方法,此方法指示了如何调用这些方法在父类中的实现。如果你看过Joshua Bloch 的Effective Java,你会发现这是错误的。如果我们在子类中添加成员变量,在不违反对称规则的情况下,我们就不能正确的实现equals()方法和compareTo()方法。我们应该使用组合的方式而不是继承。如果我们使用组合的方式构造CompanyDetails,对于Comparable接口来说没有任何问题,因为我们没有自动的实现,而且在默认的情况允许不同的行为。而且我们也能满足正确的equals()和hashCode()的需求。
这篇文章提到的问题非常普遍,但是经常被忽视。Comparable接口的问题实际是由于不好的约定和对使用的接口需求的错误理解造成的。作为一个Java开发人员或架构师,你应该特别注意这样的事情,并遵守良好的编码习惯和做法。
越大的项目,这种问题就越显得重要。这里我总结了一个使用Comparable接口的最佳实践,可以避免这个错误。
Java的Comparable接口的设计和使用的最佳实践:
·了解你需要创建的领域对象,如果对象没有明确的排序规则,请不要实现Comparable接口。
·更多的使用Comparator而不是Comparable,Comparator在更多的业务使用方式时要显得更为实用。
·如果你需要创建依赖Comparable接口的接口或者库,如果可能的话你提供自己的Comparator实现,否则就写一个良好的文档指明在你的接口实现类中如何实现。
·遵守良好的编码习惯和做法。Effective Java是很好的推荐。
- java比较器Comparable接口和Comaprator接口
Comparable故名思意是比较,意思就是做比较的,然后进行排序. 1.什么是comparable接口 此接口强行对实现它的每个类的对象进行整体排序.此排序被称为该类的自然排序 ,类的 compar ...
- java.lang.Comparable接口
转自:http://blog.csdn.net/zccst/article/details/5092920 java.lang.Comparable 接口 作者: zccst java.lang.Co ...
- java实现Comparable接口和Comparator接口,并重写compareTo方法和compare方法
原文地址https://segmentfault.com/a/1190000005738975 实体类:java.lang.Comparable(接口) + comareTo(重写方法),业务排序类 ...
- Java之comparable接口
comparable 接口: 1. 问题:java.util.Collections 类中的方法 Collections.sort(List list) 是根据什么确定容器中对象的“大小”顺序的? 2 ...
- Java之Comparable接口和Comparator接口
Comparable & Comparator 都是用来实现集合中元素的比较.排序的: Comparable 是在集合内部定义的方法实现的排序: Comparator 是在集合外部实现的排序: ...
- Java.lang.Comparable接口和Java.util.Comparator接口的区别
Java的Comparator和Comparable当需要排序的集合或数组不是单纯的数字型时,通常可以使用Comparator或Comparable,以简单的方式实现对象排序或自定义排序. 1.Com ...
- java lang(Comparable接口) 和java util(Comparator接口)分析比较
//Comparable 接口强行对实现它的每个类的对象进行整体排序. -- 自然排序.类的compareTo称为自然比较方法. public interface Comparable<T> ...
- Java中Comparable接口和Comparator接口的简单用法
对象比较器 1.Comparable接口 此接口强行对实现它的每个类的对象进行整体排序,这种排序成为类的自然排序,类的compareTo方法称为类的自然比较方法. 代码示例 import java.u ...
- java通过Comparable接口实现字符串比较大小排序的简单实例
/** * 对象比较大小compare的用法 字符串排序 * 练习代码, 给定字符串" nba" "cba" "ncaa" "wb ...
随机推荐
- CodeForces 908C. New Year and Curling 解题报告 Java
1. 思路 这题实际上是个几何问题——两个外相切的圆,由勾股定理,他们的纵坐标有以下的规律: 则有$$y_{n+1} = y_{n} + \sqrt{(2r)^2 - (x_{n} - x_{n+1} ...
- UVa 401 - Palindromes 解题报告 - C语言
1.题目大意 输入字符串,判断其是否为回文串或镜像串.其中,输入的字符串中不含0,且全为合法字符.以下为所有的合法字符及其镜像: 2.思路 (1)考虑使用常量数组而不是if或switch来实现对镜像的 ...
- ZOJ 3644 Kitty's Game(数论+DP)
Description Kitty is a little cat. She is crazy about a game recently. There arenscenes in the game( ...
- DFS中的奇偶剪枝(技巧)
剪枝是什么,简单的说就是把不可行的一些情况剪掉,例如走迷宫时运用回溯法,遇到死胡同时回溯,造成程序运行时间长.剪枝的概念,其实就跟走迷宫避开死胡同差不多.若我们把搜索的过程看成是对一棵树的遍历,那么剪 ...
- Thunder团队第六周 - Scrum会议2
Scrum会议2 小组名称:Thunder 项目名称:i阅app Scrum Master:宋雨 工作照片: 参会成员: 王航:http://www.cnblogs.com/wangh013/ 李传康 ...
- Java中I/O流之数据流
Java 中的数据流: 对于某问题:将一个 long 类型的数据写到文件中,有办法吗? 转字符串 → 通过 getbytes() 写进去,费劲,而且在此过程中 long 类型的数需要不断地转换. ...
- TCP系列20—重传—10、早期重传(ER)
一.介绍 在前面介绍thin stream时候我们介绍过有两种场景下可能不会产生足够的dup ACK来触发快速重传,一种是游戏类响应交互式tcp传输,另外一种是传输受到拥塞控制的限制,只能发送少量TC ...
- 转 【.NET平台下使用MongoDB入门教程】
目录 一.了解MongoDB 二.MongoDB特点 三.安装及常用命令 3.1 下载安装 3.2 启动服务器 3.3 常用操作 3.4 其他命令 3.5 做成windows服务 四.批处理程序开启M ...
- .net 简体转换繁体实例,繁体转换简体 Encode.dll、下载
在项目中先引用Encode.dll 下面是下载地址: Encode.dll ChineseConverter.dll 1.html页面代码 <%@ Page Language="C# ...
- lol人物模型提取(四)
在淘宝上联系了一个3d打印服务的卖家,他要我转成stl.obj.xt.xst.igs任意一种格式给他发过去,我就把它转成了obj格式给他发过去了. 然后他那边打开是这样的,没有贴图,看上去模型 ...