为了账号安全,请及时绑定邮箱和手机立即绑定

Java中摆脱equals,compareTo和toString

标签:
Java

我们都曾在POJO中重写过equals(),compareTo()和toString()方法。但是另有其他能做到职责分离的更好的方法并带来更简洁的代码。阅读这篇文章来一探究竟吧!

更简明的职责——摆脱equals、compareTo和toString方法

你曾经查看过java文档中的Object类吗?也许吧。每当你向上追溯继承树的时候都会止步于这个类。你会注意到,该类有几个方法是每一个类都必须继承的。而你最喜欢重写的方法可能就是toString().equals() and .hashCode() 这三个了。(至于为何总是应该同时重写后两个方法,请看Per-Åke Minborg写的这篇文章:https://minborgsjavapot.blogspot.com/2014/10/new-java-8-object-support-mixin-pattern.html)

但是仅仅有这几个方法显然是不够的。许多人将标准库中的其他的接口如Comparable和Serializable加以组合。但是这样真的明智吗?为什么每个人都很迫切地去自己实现这些方法呢?事实上,当你准备将对象存储在一些容器中,如HashMap,并且想要控制哈希冲突的时候,实现你自己的.equals()方法和.hashCode()方法确实有它的意义,但实现compareTo()和toString()方法又是为何呢?

本篇文章中我将提出一种使用到Speedment 开源项目上的软件设计方法,这里的对象的方法被定义为存储于变量上的方法引用,而不是重写它们。这样做确有一些好处:你的POJO将会更短小简洁,通用的方法可以不需要继承而进行复用并且你可以因地制宜地使用它们。

原始的代码

首先我们来看下面的代码:这里有一个典型的Java类Person。在使用中需要从一个Set中打印出每一个person对象,并且按照姓在前和名在后的顺序排列(以防出现两个相同姓氏的人)。

Person.java

public class Person implements Comparable<Person> {    private final String firstname;    private final String lastname;    public Person(String firstname, String lastname) {        this.firstname = firstname;        this.lastname  = lastname;
    }    public String getFirstname() {        return firstname;
    }    public String getLastname() {        return lastname;
    }    @Override
    public int hashCode() {        int hash = 7;
        hash = 83 * hash + Objects.hashCode(this.firstname);
        hash = 83 * hash + Objects.hashCode(this.lastname);        return hash;
    }    @Override
    public boolean equals(Object obj) {        if (this == obj) return true;        if (obj == null) return false;        if (getClass() != obj.getClass()) return false;        final Person other = (Person) obj;        if (!Objects.equals(this.firstname, other.firstname)) {            return false;
        }        return Objects.equals(this.lastname, other.lastname);
    }    @Override
    public int compareTo(Person that) {        if (this == that) return 0;        else if (that == null) return 1;        int comparison = this.firstname.compareTo(that.firstname);        if (comparison != 0) return comparison;
        comparison = this.lastname.compareTo(that.lastname);        return comparison;
    }    @Override
    public String toString() {        return firstname + " " + lastname;
    }
}

Main.java

public class Main {    public static void main(String... args) {        final Set
      people = new HashSet<>();
        people.add(new Person("Adam", "Johnsson"));
        people.add(new Person("Adam", "Samuelsson"));
        people.add(new Person("Ben", "Carlsson"));
        people.add(new Person("Ben", "Carlsson"));
        people.add(new Person("Cecilia", "Adams"));
        people.stream()
            .sorted()
            .forEachOrdered(System.out::println);
    }
}

Output

run:
Adam Johnsson
Adam Samuelsson
Ben Carlsson
Cecilia Adams
BUILD SUCCESSFUL (total time: 0 seconds)

Person 类实现了一些方法来控制输出。 hashCode()equals() 方法确保同一个person对象不会被重复添加到set中。.compareTo() 方法用于排序方法中生成应有的顺序。而重写方法toString()是在System.out.println() 被调用的时候控制每个Person对象的输出格式。你认出这种结构了吗?几乎任何一个java工程中都会有它。

替代这些代码

相比于将所有这些方法写入Person类中,我们可以让它保持尽量的简洁,使用方法引用去处理它们。我们可以删除所有equals(),hashCode(),compareTo()和toString()的样板式代码,取而代之的是下面介绍的两个静态变量:COMPARATOR 和TO_STRING

Person.java

public class Person {    private final String firstname;    private final String lastname;    public Person(String firstname, String lastname) {        this.firstname = firstname;        this.lastname  = lastname;
    }    public String getFirstname() {        return firstname;
    }    public String getLastname() {        return lastname;
    }    public final static Comparator<Person> COMPARATOR =
        Comparator.comparing(Person::getFirstname)
            .thenComparing(Person::getLastname);    public final static Function<Person, String> TO_STRING =
        p -> p.getFirstname() + " " + p.getLastname();
}

Main.java

public class Main {    public static void main(String... args) {        final Set
      people = new TreeSet<>(Person.COMPARATOR);
        people.add(new Person("Adam", "Johnsson"));
        people.add(new Person("Adam", "Samuelsson"));
        people.add(new Person("Ben", "Carlsson"));
        people.add(new Person("Ben", "Carlsson"));
        people.add(new Person("Cecilia", "Adams"));
        people.stream()
            .map(Person.TO_STRING)
            .forEachOrdered(System.out::println);
    }
}

Output

run:
Adam Johnsson
Adam Samuelsson
Ben Carlsson
Cecilia Adams
BUILD SUCCESSFUL (total time: 0 seconds)

这样实现的好处是我们可以在不用更改Person类的情况下替换排序策略或打印格式。这将使代码拥有更强的可维护性和复用性,更不用说更快的编写速度了。

译文链接:http://www.codeceo.com/article/java-equals-compareto-tostring.html
英文原文:Get Rid of Equals, CompareTo and toString
翻译作者:码农网 – 单劼

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消