立即注册
 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
广州大学城网业务调整

[Java/JSP] Java的Comparable接口的一个陷阱 [复制链接] qrcode

查看: 3393 | 回复: 0

大法师的 该用户已被删除
发表于: 2012-11-14 17:44:46 | 显示全部楼层

  Java Comparable接口来提供该接口的一个实现对象排序列表的方法。最初的秩序对于简单的对象是很有意义的,但是当我们面对复杂的面向对象的业务逻辑对象,事情变得更加复杂。从业务的角度来看,一些交易对象的自然顺序可能按照交易值排序,但是从系统管理员的角度来看,这种规则可能被交易的速度。所以在大多数情况下,并没有明确的业务对象的自然排序规则。
  我们发现一个需要排序,例如公司。我们公司官方名称作为一个主键,ID作为次要的关键词。这个类执行:
  1. public class Company implements Comparable {
  2.
  3. private final String id;
  4. private final String officialName;
  5.
  6. public Company(final String id, final String officialName) {
  7. this.id = id;
  8. this.officialName = officialName;
  9. }
  10.
  11. public String getId() {
  12. return id;
  13. }
  14.
  15. public String getOfficialName() {
  16. return officialName;
  17. }
  18.
  19. @Override
  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();
  25. }
  26.
  27. @Override
  28. public boolean equals(final Object obj) {
  29. if (obj == this) {
  30. return true;
  31. }
  32. if (!(obj instanceof Company)) {
  33. return false;
  34. }
  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();
  40. }
  41.
  42. @Override
  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();
  48. }
  49. }
  这个实现看起来没问题,假设现在这个类提供的信息不够使用,我们又创建了这个类的一个子类CompanyDetail类用以扩展他。例如我们想以一个表的形式显示公司的信息,我们就可以用这个类。
  1. public class CompanyDetails extends Company {
  2.
  3. private final String marketingName;
  4. private final Double marketValue;
  5.
  6. public CompanyDetails(final String id, final String officialName, final String marketingName, final Double marketValue) {
  7. super(id, officialName);
  8. this.marketingName = marketingName;
  9. this.marketValue = marketValue;
  10. }
  11.
  12. public String getMarketingName() {
  13. return marketingName;
  14. }
  15.
  16. public Double getMarketValue() {
  17. return marketValue;
  18. }
  19.
  20. @Override
  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();
  26. }
  27.
  28. @Override
  29. public boolean equals(final Object obj) {
  30. if (obj == this) {
  31. return true;
  32. }
  33. if (!(obj instanceof CompanyDetails)) {
  34. return false;
  35. }
  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();
  42. }
  43. }
  这个类的实现看起来还是没什么问题,但是事实上是有问题的,我们可以写一个test指出问题在哪里。当我们没有对父类的所有细节加以注意时,问题就来了。
  1. CompanyDetails c1 = new CompanyDetails("231412", "McDonalds Ltd", "McDonalds food factory", 120000.00);
  2. CompanyDetails c2 = new CompanyDetails("231412", "McDonalds Ltd", "McDonalds restaurants", 60000.00);
  3.
  4. Set set1 = CompaniesFactory.createCompanies1();
  5. set1.add(c1);
  6. set1.add(c2);
  7.
  8. Set set2 = CompaniesFactory.createCompanies2();
  9. set2.add(c1);
  10. set2.add(c2);
  11.
  12. Assert.assertEquals(set1.size(), set2.size());
  我们构建两集,但结果是断言结果是不平等的。这是为什么呢?其中的一个set是一个HashSet,他依靠的对象(hashCode)和equals()方法,但另一个是TreeSet,他只是依靠Comparable接口,这个接口在我们没有意识到子类。在字段的对象是扩展当它是一个常见的错误,但更重要的是,它不是好的编码约定造成。我们使用Apache Commons软件包在生成器中实现hashCode(),equals()和compareTo()方法。构建器提供了appendSuper()方法,该方法显示如何调用这些方法在它的父类的实现。如果你阅读Joshua Bloch有效的Java,您会发现这是错误的。如果我们添加一个成员变量的子类,在不违反对称规则,我们不能正确实现equals()方法和compareTo()方法。我们应该使用组合的方式而不是继承。如果我们使用组合方法CompanyDetails构造,为Comparable接口没有任何问题,因为我们没有自动实现,而且在默认允许不同的行为。而且我们还可以见到正确的equals()和hashCode(需求)。
  这篇文章是指问题是很常见的,但经常被忽视。Comparable接口问题是由于坏的同意和使用错误的理解产生的界面需求。作为一个Java开发人员或架构师,你应该特别注意这些事情,并遵循良好的编程习惯和做法。更大的项目,这个问题更重要。我在这里总结了使用Comparable接口最佳实践,可以避免这个错误。
  Java Comparable接口设计和最佳实践的使用:
  了解您需要创建域对象,如果对象不是一个清晰的排名规则,请不要实现Comparable接口。
  更多的使用比较器,而不是•可比性,比较器在更多的商业使用出现更实用。
  •如果您需要创建一个类似的接口或库的依赖,如果你们有可能提供自己的实现的比较器,或者编写一个好的文档中指定接口实现类如何实现。
  •遵循良好的编程习惯和做法。有效的Java是一个很好的建议。
  QQ 744437114
  疯狂软件官网:www.fkjava.org
  疯狂java视频 android视频:http://www.fkjava.org/video.html
跳转到指定楼层
快速回复 返回顶部 返回列表