Guava进修笔记:复写的Object常用办法

    添加时间:2013-6-18 点击量:

      在Java中Object类是所有类的父类,此中有几个须要override的办法比如equals,hashCode和toString等办法。每次写这几个办法都要做很多反复性的断定, 很多类库供给了覆写这几个办法的对象类, Guava也供给了类似的体式格式。下面我们来看看Guava中这几个办法简单应用。


      equals办法:


      equals是一个经常须要覆写的办法, 可以查看Object的equals办法注释, 对equals有几个性质的请求:
        1. 自反性reflexive:任何非空引用x,x.equals(x)返回为true;
        2. 对称性symmetric:任何非空引用x和y,x.equals(y)返回true当且仅当y.equals(x)返回true;
        3. 传递性transitive:任何非空引用x和y,若是x.equals(y)返回true,并且y.equals(z)返回true,那么x.equals(z)返回true;
        4. 一致性consistent:两个非空引用x和y,x.equals(y)的多次调用应当对峙一致的成果,(前提前提是在多次斗劲之间没有批改x和y用于斗劲的相干信息);
        5. 对于所有非null的值x, x.equals(null)都要返回false。 (若是你要用null.equals(x)也可以,会报NullPointerException)。


      要覆写的类中某些值可能为null的时辰,就须要对null做很多断定和分支处理惩罚。 应用Guava的Objects.equal办法可以避免这个题目, 使得equals的办法的覆写变得加倍轻易, 并且可读性强,简洁优雅。



    import org.junit.Test;
    
    import com.google.common.base.Objects;

    public class ObjectTest {

    @Test
    public void equalTest() {
    System.out.println(Objects.equal(
    a, a));
    System.out.println(Objects.equal(
    null, a));
    System.out.println(Objects.equal(
    a, null));
    System.out.println(Objects.equal(
    nullnull));
    }

    @Test
    public void equalPersonTest() {
    System.out.println(Objects.equal(
    new Person(peida,23), new Person(peida,23)));
    Person person
    =new Person(peida,23);
    System.out.println(Objects.equal(person,person));
    }
    }

    class Person {
    public String name;
    public int age;

    Person(String name,
    int age) {
    this.name = name;
    this.age = age;
    }
    }


      运行输出:



    true
    
    false
    false
    true
    false
    true


      hashCode办法:


      当覆写(override)了equals()办法之后,必须也覆写hashCode()办法,反之亦然。这个办法返回一个整型值(hash code value),若是两个对象被equals()办法断定为相等,那么它们就应当拥有同样的hash code。Object类的hashCode()办法为不合的对象返回不合的值,Object类的hashCode值默示的是对象的地址。
      hashCode的一般性契约(须要满足的前提)如下:
      1.在Java应用的一次履行过程中,若是对象用于equals斗劲的信息没有被批改,那么同一个对象多次调用hashCode()办法应当返回同一个整型值。应用的多次履行中,这个值不须要对峙一致,即每次履行都是对峙着各自不合的值。
      2.若是equals()断定两个对象相等,那么它们的hashCode()办法应当返回同样的值。
      3.并没有强迫请求若是equals()断定两个对象不相等,那么它们的hashCode()办法就应当返回不合的值。即,两个对象用equals()办法斗劲返回false,它们的hashCode可以雷同也可以不合。然则,应当意识到,为两个不相等的对象产生两个不合的hashCode可以改良哈希表的机能。
      写一个hashCode底本也不是很难,然则Guava供给给我们了一个加倍简单的办法--Objects.hashCode(Object ...), 这是个可变参数的办法,参数列表可所以随便率性数量,所以可以像如许应用Objects.hashCode(field1, field2, ..., fieldn)。很是便利和简洁。


      



    import org.junit.Test;
    
    import com.google.common.base.Objects;

    public class ObjectTest {
    @Test
    public void hashcodeTest() {
    System.out.println(Objects.hashCode(
    a));
    System.out.println(Objects.hashCode(
    a));
    System.out.println(Objects.hashCode(
    a,b));
    System.out.println(Objects.hashCode(
    b,a));
    System.out.println(Objects.hashCode(
    a,b,c));

    Person person
    =new Person(peida,23);
    System.out.println(Objects.hashCode(person));
    System.out.println(Objects.hashCode(person));
    }
    }

    class Person {
    public String name;
    public int age;

    Person(String name,
    int age) {
    this.name = name;
    this.age = age;
    }
    }



    128
    
    4066
    4096
    126145
    19313256
    19313256


      toString()办法:


      因为每个类都直接或间接地持续自Object,是以每个类都有toString()办法。这个办法是用得最多的, 覆写得最多, 一个好的toString办法对于调试来说是很是首要的, 然则写起来确切很不爽。Guava也供给了toString()办法。


      



    import org.junit.Test;
    
    import com.google.common.base.Objects;

    public class ObjectTest {

    @Test
    public void toStringTest() {
    System.out.println(Objects.toStringHelper(
    this).add(x, 1).toString());
    System.out.println(Objects.toStringHelper(Person.
    class).add(x, 1).toString());

    Person person
    =new Person(peida,23);
    String result
    = Objects.toStringHelper(Person.class
    .add(
    name, person.name)
    .add(
    age, person.age).toString();
    System.out.print(result);
    }
    }

    class Person {
    public String name;
    public int age;

    Person(String name,
    int age) {
    this.name = name;
    this.age = age;
    }
    }

    //============输出===============
    ObjectTest{x=1}
    Person{x
    =1}
    Person{name
    =peida, age=23}


      compare/compareTo办法:


      CompareTo:compareTo(Object o)办法是java.lang.Comparable<T>接口中的办法,当须要对某个类的对象进行排序时,该类须要实现 Comparable<T>接口的,必须重写public int compareTo(T o)办法。java规定,若a,b是两个对象,当a.compareTo(b)>0时,则a大于b,a.compareTo(b)<0时,a<b,即规定对象的斗劲大小的规矩;
      compare: compare(Object o1,Object o2)办法是java.util.Comparator<T>接口的办法,compare办法内首要靠定义的compareTo规定的对象大小关系规矩来断定对象的大小。


      compareTo办法的通用商定与equals类似:将本对象与指定的对象停止对比,若是本对象小于、便是、或大于指定对象,则分别返回正数、零、或正数。若是指定的对象类型无法与本对象停止对比,则跑出ClassCastException。
      对称性:实现者必须包管对全部的x和y都有sgn(x.compareTo(y)) == -sgn(y.compareTo(x))。这也暗示当且仅当y.compareTo(x)抛出异常时,x.compareTo(y)才抛出异常。
      传递性:实现者必须包管对比关系是可传递的,若是x.compareTo(y) > 0且y.compareTo(z) > 0,则x.compareTo(z) > 0。实现者必须包管x.compareTo(y)==0暗示着全部的z都有(x.compareTo(z)) == (y.compareTo(z))。
      虽不强迫请求,但强烈建议(x.compareTo(y) == 0) == (x.equals(y))。一般来说,任何实现了Comparable的类若是违背了这个商定,都应当熟悉打听申明。推荐这么说:“重视:本类拥有天然次序,但与equals不一致”。
      第一条指出,若是倒置两个对比对象的对比次序,就会产生以下景象:若是第一个对象小于第二个对象,则第二个对象必须大于第一个对象;若是第一个对象便是第二个对象,则第二个对象也必须便是第一个对象;若是第一个对象大于第二个对象,则第二个对象小于第一个对象。
      第二条指出,若是第一个对象大于第二个对象,第二个对象大于第三个对象,则第一个大于第三个。
      第三条指出,对于两个相当的对象,他们与其他任何对象对比成果应当类似。
      这三公商定的一个成果是,compareTo办法的等同性测试必须与equals办法合意类似的束缚前提:自反性、对称性、传递性。所以也存在类同的束缚:不克不及在扩大一个可实例化的类并添加新的值组件时,同时包管compareTo的商定,除非你愿意放弃面向对象抽象的上风。可以用与equals类似的规避办法:若是想在实现Comparable接口的类中增长一个值组件,就不要扩大它;应当写一个不相干的类,此中包含第一个类的实例。然后供给一个view办法返回该实例。如许你就可以再第二个类上实现任何compareTo办法,同时容许客户在必要的时辰将第二个类算作是第一个类的一个实例。
      compareTo商定的最后一段是一个强烈的建议而非真正的商定,即compareTo办法的等同性测试必须与equals办法的成果类似。若是遵守了这一条,则称compareTo办法所施加的次序与equals一致;反之则称为与equals不一致。当然与equals不一致的compareTo办法仍然是可以工作的,然则,若是一个有序凑集包含了该类的元素,则这个凑集可能就不克不及遵守响应凑集接口(Collection、Set、Map)的通用商定。这是因为这些接口的通用商定是基于equals办法的,然则有序凑集却应用了compareTo而非equals来履行。


      下面我们简单本身实现一个类的compareTo办法:



    import org.junit.Test;
    

    public class ObjectTest {


    @Test
    public void compareTest(){
    Person person
    =new Person(peida,23);
    Person person1
    =new Person(aida,25);
    Person person2
    =new Person(aida,25);
    Person person3
    =new Person(aida,26);
    Person person4
    =new Person(peida,26);

    System.out.println(person.compareTo(person1));
    System.out.println(person1.compareTo(person2));
    System.out.println(person1.compareTo(person3));
    System.out.println(person.compareTo(person4));
    System.out.println(person4.compareTo(person));
    }
    }

    class Person implements Comparable<Person>{
    public String name;
    public int age;

    Person(String name,
    int age) {
    this.name = name;
    this.age = age;
    }

    @Override
    public int compareTo(Person other) {
    int cmpName = name.compareTo(other.name);
    if (cmpName != 0) {
    return cmpName;
    }
    if(age>other.age){
    return 1;
    }
    else if(age<other.age){
    return -1;
    }
    return 0;
    }
    }



    //========输出===========
    15
    0
    -1
    -1
    1


      上方的compareTo办法,代码看上去并不是十分优雅,若是实体属性很多,数据类型雄厚,代码可读性将会很差。在guava里, 对所有原始类型都供给了斗劲的对象函数来避免这个麻烦. 比如对Integer, 可以用Ints.compare()。哄骗guava的原始类型的compare,我们对上方的办法做一个简化,实现compare办法:



    class PersonComparator implements Comparator<Person> {  
    
    @Override
    public int compare(Person p1, Person p2) {
    int result = p1.name.compareTo(p2.name);
    if (result != 0) {
    return result;
    }
    return Ints.compare(p1.age, p2.age);
    }
    }


      上方的代码看上去简单了一点,但还是不那么优雅简单,对此, guava有一个相当聪慧的解决办法, 供给了ComparisonChain:



    class Student implements Comparable<Student>{
    
    public String name;
    public int age;
    public int score;

    Student(String name,
    int age,int score) {
    this.name = name;
    this.age = age;
    this.score=score;
    }

    @Override
    public int compareTo(Student other) {
    return ComparisonChain.start()
    .compare(name, other.name)
    .compare(age, other.age)
    .compare(score, other.score, Ordering.natural().nullsLast())
    .result();
    }
    }

    class StudentComparator implements Comparator<Student> {
    @Override
    public int compare(Student s1, Student s2) {
    return ComparisonChain.start()
    .compare(s1.name, s2.name)
    .compare(s1.age, s2.age)
    .compare(s1.score, s2.score)
    .result();
    }
    }
    }


      ComparisonChain是一个lazy的斗劲过程, 当斗劲成果为0的时辰, 即相等的时辰, 会持续斗劲下去, 呈现非0的景象, 就会忽视后面的斗劲。ComparisonChain实现的comparecompareTo在代码可读性和机能上都有很大的进步。


      下面来一个综合应用实例:



    import java.util.Comparator;
    

    import org.junit.Test;

    import com.google.common.base.Objects;
    import com.google.common.collect.ComparisonChain;
    import com.google.common.collect.Ordering;

    public class ObjectTest {


    @Test
    public void StudentTest(){

    Student student
    =new Student(peida,23,80);
    Student student1
    =new Student(aida,23,36);
    Student student2
    =new Student(jerry,24,90);
    Student student3
    =new Student(peida,23,80);

    System.out.println(
    ==========equals===========);
    System.out.println(student.equals(student2));
    System.out.println(student.equals(student1));
    System.out.println(student.equals(student3));

    System.out.println(
    ==========hashCode===========);
    System.out.println(student.hashCode());
    System.out.println(student1.hashCode());
    System.out.println(student3.hashCode());
    System.out.println(student2.hashCode());

    System.out.println(
    ==========toString===========);
    System.out.println(student.toString());
    System.out.println(student1.toString());
    System.out.println(student2.toString());
    System.out.println(student3.toString());

    System.out.println(
    ==========compareTo===========);
    System.out.println(student.compareTo(student1));
    System.out.println(student.compareTo(student2));
    System.out.println(student2.compareTo(student1));
    System.out.println(student2.compareTo(student));

    }

    }

    class Student implements Comparable<Student>{
    public String name;
    public int age;
    public int score;


    Student(String name,
    int age,int score) {
    this.name = name;
    this.age = age;
    this.score=score;
    }

    @Override
    public int hashCode() {
    return Objects.hashCode(name, age);
    }


    @Override
    public boolean equals(Object obj) {
    if (obj instanceof Student) {
    Student that
    = (Student) obj;
    return Objects.equal(name, that.name)
    && Objects.equal(age, that.age)
    && Objects.equal(score, that.score);
    }
    return false;
    }

    @Override
    public String toString() {
    return Objects.toStringHelper(this
    .addValue(name)
    .addValue(age)
    .addValue(score)
    .toString();
    }


    @Override
    public int compareTo(Student other) {
    return ComparisonChain.start()
    .compare(name, other.name)
    .compare(age, other.age)
    .compare(score, other.score, Ordering.natural().nullsLast())
    .result();
    }
    }



    class StudentComparator implements Comparator<Student> {
    @Override
    public int compare(Student s1, Student s2) {
    return ComparisonChain.start()
    .compare(s1.name, s2.name)
    .compare(s1.age, s2.age)
    .compare(s1.score, s2.score)
    .result();
    }
    }

    //=============运行输出===========================
    ==========equals===========
    false
    false
    true
    ==========hashCode===========
    -991998617
    92809683
    -991998617
    -1163491205
    ==========toString===========
    Student{peida,
    23, 80}
    Student{aida,
    23, 36}
    Student{jerry,
    24, 90}
    Student{peida,
    23, 80}
    ==========compareTo===========
    1
    1
    1
    -1


      

    我们永远不要期待别人的拯救,只有自己才能升华自己。自己已准备好了多少容量,方能吸引对等的人与我们相遇,否则再美好的人出现、再动人的事情降临身边,我们也没有能量去理解与珍惜,终将擦肩而过。—— 姚谦《品味》
    分享到: