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 |
|