1 前言
Java 是一门集大成的面向对象语言, 在Java的世界里, 一切皆对象, 而Object
类就是所有对象的默认父类. Guava
提供了若干个工具方法来扩展Object
类的通用能力.
2 equals
在Java的编程世界, 比较两个对象是个很常见的操作, Object
类也提供了一个equals
方法来判断对象是否相等.
但是Object
使用的equals
方法有诸多不便, 最痛苦的是无处不在的NullPointerException
, 例如:
public testEqueal(Object input){
this.equals(input);
}
但当 this
指针指向一个空对象的时候,
就会出现null.testEqueal(input)
的情形, 就会抛出NPE.
为了让equals
方法更易用,
Guava提供了一个Objects.equal(Object a, Object b)
方法来判断两个对象是否相等.
用法如下:
Objects.equal("a", "a"); // returns true
Objects.equal(null, "a"); // returns false
Objects.equal("a", null); // returns false
Objects.equal(null, null); // returns true
可能是Java语言的开发者也意识到Object.equals
方法的不便,
所以在JDK7的时候,
官方也提供了Objects.equals(Object a, Object b)
的方法,
Guava的竞品自然也没了用武之处。
不过, 说实话, 无论是JDK的Objects.equals
, Object.equal
还是Guava的Object.equal()
,
在日常的开发中也用的不多, 用的最多的是Apache Common库的各种Utils工具,
比较String类型用的是StringUtils.equals()
,
比较容器(Collection)用的CollectionUtils.isEqualCollection()
,
毕竟这些工具要更高级和完善(有趣的是, JDK的方法名是equals
,
Guava的方法名是equal
, 下文提到的JDK的hash方法名叫hash
,
Guava叫hashCode
).
2.1 hashCode
在《Effective Java》的条款9中说到:
Always override
hashCode
when you overrideequals
就是说在你重写equals
方法的时候, 记得重写hashCode
方法,
因为按照Java的约定, 如果两个对象通过调用equals
方法判断是相等的话,
它们调用hashCode()
方法的返回结果也是一样的.
《Effective Java》 给出的重写建议是把一个对象的所有字段进行计算取得一个hash值, 示例代码如下:
private volatile int hashCode;
public class User {
private String name;
private int age;
private String passport;
@Override
public int hashCode() {
int result = hashCode;
if(result == 0){
result = 17; // Aribtrary number.
result = 31 * result + name.hashCode();
result = 31 * result + age; // 31 is an odd prime
result = 31 * result + passport.hashCode();
hashCode = result;
}
return hashCode;
}
}
这样的计算方式虽然有效, 但是未免过于烦琐, 还要手动计算每个字段. 为此,
Guava 提供了一个 Objects.hashCode(field1, field2, ..., fieldn
的方法,
用于对所有的字段计算hash值, 用法如下:
public class User {
public int hashCode() {
return Objects.hashCode(name, age, passport);
}
}
看起来简洁多了。然后在Java7的时候,
JDK也推出了一个Objects.hash(field1, field2,...,fieldn)
的方法,
而Guava的竞品很快就被废弃了. 我都在想JDK是不是在吸收Guava的精华,
毕竟实现都一样!( ̄▽ ̄)
3 toString
《Effective Java》的条款10说到:
Always override toString
也就是说, 《Effective Java》建议所有的类都重写toString()
方法.
其实toString()
方法不是给程序看的, 而是给开发者自己看的.
据说, 好看的toString()
方法的输出结果可以让程序员更愉悦, 可见颜值处处都有用.
比较常见的重写toString()
的方式是把所有的字段拼接输出,
只不过手动拼接有点累.
省心的是, Intellij Idea 为开发者提供了生成toString()
的快捷方式, 如下图:
如果觉得Idea生成的toString()
有太多的拼接字符串,
还可以试试Guava提供的toString()
工具方法: MoreObjects.toStringHelper
,
具体用法如下:
@Test
public void test() {
System.out.println(MoreObjects.toStringHelper(this)
.add("name", "Linus")
.toString());
System.out.println(MoreObjects.toStringHelper("TestToStringHelper")
.add("method", "toStringHelper")
.toString());
}
// 结果如下:
// ToStringTest{name=Linus}
// TestToStringHelper{method=toStringHelper}
使用方法也是很明了, 就不过多赘述.
4 compare/compareTo
既然前面提到了《Effective Java》, 那么基于前后呼应的原则, 最后也免不了要再引用一下《Effective Java》:
条款12: Consider implementing Comparable
不像前文介绍过的方法, compareTo
方法并不是Object
类的方法,
而是Comparable
接口的方法. 这个方法和前文提到的equals
方法类似,
只不过用法不一样. compareTo
通常用于排序,
如下面的代码就是对实现了Comparable接口的Person对象的列表进行排序:
class Person implements Comparable<Person> {
private String lastName;
private String firstName;
private int zipCode;
public int compareTo(Person other) {
int cmp = lastName.compareTo(other.lastName);
if (cmp != 0) {
return cmp;
}
cmp = firstName.compareTo(other.firstName);
if (cmp != 0) {
return cmp;
}
return Integer.compare(zipCode, other.zipCode);
}
}
public class CompareTest {
@Test
public void testSort(){
Person[] persons = new Person[2];
persons[0] = new Person("Ma", "Jack", 12345);
persons[1] = new Person("Ma", "Pony", 65432);
Arrays.sort(persons);
Arrays.stream(persons).forEach(person -> System.out.println(person.getFirstName()));
}
}
上面的代码的逻辑就是先比较Person.lastName
,
如果相等再比较Person.firstName
, 如果前面的条件还是相等,
就再比较Person.zipCode
.
代码的含义相当清晰, 只是有不少的模板代码, 如果能减少这些模板代码, 那就更好了. 幸运的是, Guava 提供了一个
ComparisonChain
来处理这些模板逻辑, 应用ComparisonChain
之后的代码如下:
public class ComparisonChainPerson implements Comparable<ComparisonChainPerson> {
private String lastName;
private String firstName;
private int zipCode;
@Override
public int compareTo(ComparisonChainPerson that) {
return ComparisonChain.start()
.compare(this.lastName, that.lastName)
.compare(this.firstName, that.firstName)
.compare(this.zipCode, that.zipCode)
.result();
}
}
确实简洁了许多~~
5 总结
到本文为止, Guava提供的基本工具类就已经介绍完了,暂时告一段落了, 接下来就要介绍Guava最常用的工具之一: 各种容器(Collections)