从避免使用null开始
由于null的根本问题在于含糊而不明确,要避免使用null的方式,就是确认过去使用null的时机与目的,并使用明确的语义。在过去使用null的情况中,开发者在方法中传回null,通常代表着客户端必须检查是否为null,并在null的情况下使用预设值,以便程序能够继续执行。举个例子来说,如果原先有getNickName方法可以传回String,调用它如下:
1
2
3
4
5
String nickName = getNickName ( "Duke" );
if ( nickName == null ){
nickName = "Ajia's User" ;
}
out . println ( nickName );
如果客户端忘了检查null,那么就会直接显示null,在这个简单的例子中并不会怎么样,只是显示的结果令人困惑罢了,但如果后面的执行流程牵涉到至关重要的结果,程序快乐的继续执行下去,错误可能到最后才会呈现发生。
那么可将getNickName修改使一定传回Optional(com.google.common.base)实例,但绝对不要传回null。Optional的语义是它可能包含也可能不包括值,如果你呼叫直接呼叫它的get方法:
1
2
String nickName = getNickName ( "Duke" ). get ();
out . println ( nickName );
在Optional没有包含值的情况下,就会直接抛出IllegalStateException,这实现了速错的概念,开发者可以立即发现错误,并了解到必须作出些检查,可能的方式之一像是:
1
2
3
Optional < String > nick = getNickName ( "Duke" );
String nickname = nick . isPresent ()? nick . get (): "CodeData User" ;
out . println ( nickName );
不过这看来有点啰嗦,一个比较好的方式可以是:
1
2
String nickName = getNickName ( "Duke" ). or ( "Ajia's User" );
out . println ( nickName );
在getNickName方法内部,原先如果是这样传回值:
1
return rs . next ()? rs . getString ( "nickname" ): null ;
则可以使用Optional改为:
1
return rs . next ()? Optional . of ( rs . getString ( "nickname" )): Optional . absent ();
Optional.of方法用来建立Optional对象包含传入的值,而Optional.absent建立的对象不包含任何值,也就是如果你直接调用后者建立的Optional对象上get方法,就会抛出异常。在有值的情况下使用Optional.of,在原本会传回null的情况下使用Optional.absent。
当然,过去许多程序库中使用了不少的null,这些程序无法说改就改,Guava提供了一些衔接程序库中null的方法。例如,如果原先的getNickName是你无法修改的,那么可以这么修改客户端:
1
2
String nickName = Optional . fromNullable ( getNickName ( "Duke" )). or ( "Ajia's User" );
out . println ( nickName );
Optional.fromNullable在传入值为null的情况下,传回的Optional实例调用其get方法,就会抛出错误。
使用Preconditions作参数的与判断
Guava中提供了一个工作参数检查的工具类-Preconditions,静态导入这个类,可以大大地简化代码中对于参数的预判断和处理。
1
import static com . google . common . base . Preconditions .*;
在以前,我们需要判断一个参数不为空需要像下面这样写:
1
2
3
4
5
6
public void testMethod ( Object obj ){
if ( obj == null ){
throw new NullPointerException ();
}
//... other operations
}
每次都要添加if语句来做判断,重复的工作会做好多次,使用Preconditions可以简化成下面这样
1
2
3
4
public void testMethod ( Object obj ){
Object other = checkNotNull ( obj );
//... other operations
}
checkNotNull会检查参数是否为null,当为null的时候会抛出NullPointerException,否则直接返回参数。
checkNotNull, checkArgument和checkState,都有三种形式的参数:
* public static T checkNotNull(T reference),只包含需要判断的对象,无其他多余的参数,抛出的异常不带有任何异常信息
* public static T checkNotNull(T reference, @Nullable Object errorMessage),只包含一个错误信息的额外参数,抛出的异常带有errorMessage.toString()的异常信息
* public static T checkNotNull(T reference, @Nullable String errorMessageTemplate, @Nullable Object…errorMessageArgs),这种是printf风格的错误信息,后面是变参,errorMessageTemplate可以使用一些占位符,例如可以这样写
1
2
checkArgument ( i >= 0 , "Argument was %s but expected nonnegative" , i );
checkArgument ( i < j , "Expected i<j, but %s>%s" , i , j );
捕获异常后可以获取自定义的详细错误信息,对于调试来说很有帮助,而且代码也很简洁。例如,
1
2
3
4
5
6
Object obj = null ;
try {
checkNotNull ( obj , "cannot be null" );
} catch ( Exception e ){
System . out . println ( e . getMessage ());
}
运行后可以获得自定义的异常信息“cannot be null”
Preconditions里面的方法还有下面几个
checkArgument(boolean) 检查boolean是否为真,用作方法中检查参数
checkNotNull(T)检查value不为null,直接返回value
checkState(boolean)检查对象的一些状态,不依赖方法参数。例如,Iterator可以用来next是否在remove之前被调用
checkElementIndex(int index, int size)检查index是否为在一个长度为size的list、string或array合法的范围。index的范围区间是[0,size)(包含0不包含size)。无需直接传入list、string或array,只需传入大小。 返回index
checkPositionIndex(int index, int size)检查位置index是否为在一个长度为size的list、string或array合法的范围。index的范围区间是[0, size)(包含0不包含size). 无需直接传入list, string或array, 只需传入大小
checkPositionIndexes(int start, int end, int size),检查[start, end)是一个长度为size的list, string或array合法的范围子集.伴随着错误信息.
Guava的preconditions有这样几个有点:
* 在静态导入后,方法很明确无歧义,checkNotNull可以清楚地告诉你他是干什么的,它会抛出怎样的异常
* checkNotNull在验证通过后直接返回,可以这样方便地写代码:this.field = checkNotNull(field).
* 简单而又强大的可变参数printf风格的自定义错误信息。
建议将preconditions放置在单独的行上,这样可以在调试的时候清楚地指明哪个precondition出现错误。另外,提供有帮助的错误信息也非常有用。
Ordering简介
Ordering是Guava最常用的Comparator类,可以用来操作、扩展和使用comparators。自定义比较器,进行排序判断,进行排序,获取最大值最小值,获取最大最小的前几个值等方法。
几个常用的static方法,提供了三种常用的比较器
natural() 使用Comparable类型的自然排序,例如,整数从小到大,字符串是按字典顺序
usingToString() 使用toString()返回的字符串按字典顺序进行排序
使用Ordering.from(Comparator)从一个已经存在的Comparator来构建Ordering实例
自定义Ordering
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.util.Arrays ;
import java.util.List ;
import com.google.common.collect.Ordering ;
import com.google.common.primitives.Ints ;
public class Demo {
public static void main ( String [] args ) {
Ordering < String > byLengthOrdering = new Ordering < String >() {
public int compare ( String left , String right ) {
return Ints . compare ( left . length (), right . length ());
}
};
List < String > strList = Arrays . asList ( "abc" , "a" , "bcd" );
System . out . println ( byLengthOrdering . reverse (). isOrdered ( strList ));
List < String > strList1 = Arrays . asList ( "a" , "ab" , "bcd" );
System . out . println ( byLengthOrdering . isOrdered ( strList1 ));
}
}
操作方法
reverse()方法,获取了Ordering的反排序。使用自定义的Ordering判断collection是否符合自定义顺序。
reverse() 返回与当前Ordering相反的排序
nullsFirst() 返回一个将null放在non-null元素之前的Ordering,其他的和原始的Ordering一样
compound(Comparator)返回一个使用Comparator的Ordering, Comparator作为第二排序元素,例如对bug列表进行排序,先根据bug的级别,再根据优先级进行排序。
lexicographical() Returns an Ordering that orders iterables lexicographically by their elements.
onResultOf(Function)将function应用在各个元素上之后,再使用原始ordering进行排序。
假如我们希望按sortedBy字段进行排序,
1
2
3
4
5
6
7
8
9
class Foo {
@Nullable String sortedBy ;
int notSortedBy ;
}
Ordering < Foo > ordering = Ordering . natural (). nullsFirst (). onResultOf ( new Function < Foo , String >() {
public String apply ( Foo foo ) {
return foo . sortedBy ;
}
});
还有一些很有用的方法
* greatestOf(Iterable iterable, int k) Returns the k greatest elements of the specified iterable, according to this ordering, in order from greatest to least. Not necessarily stable.
* isOrdered(Iterable) Tests if the specified Iterable is in nondecreasing order according to this ordering.
* sortedCopy(Iterable) Returns a sorted copy of the specified elements as a List.
* min(E, E) Returns the minimum of its two arguments according to this ordering. If the values compare as equal, the first argument is returned.
* min(E, E, E, E…) Returns the minimum of its arguments according to this ordering. If there are multiple least values, the first is returned.
* min(Iterable) Returns the minimum element of the specified Iterable. Throws a NoSuchElementException if the Iterable is empty.
复写Object的常用方法
在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;
* 传递性transitive:任何非空引用x和y,如果x.equals(y)返回true,并且y.equals(z)返回true,那么x.equals(z)返回true;
* 一致性consistent:两个非空引用x和y,x.equals(y)的多次调用应该保持一致的结果,(前提条件是在多次比较之前没有修改x和y用于比较的相关信息);
* 对于所有非null的值x,x.equals(null)都要返回false
当我们要覆写的类中某些值可能为null的时候,就需要对null做很多判断和分支处理。使用Guava的Object.equal方法可以避免这个问题,使得equals的方法的覆写变得更加容易,而且可读性强,简洁优雅。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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 ( null , null ));
}
@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()方法,反之亦然。这个方法返回一个整型值,如果两个对象被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 …),这是个可变参数的方法,参数列表可以是任意数量,所以可以像这样使用Object.hashCode(field1, field2, … ,fieldn)。非常方便和简洁。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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 ;
}
}
toString()
因为每个类都直接或间接地继承自Object,因此每个类都有toString()方法。这个方法是用得最多的,覆写得最多,一个好的toString方法对于调试来说是非常重要的,但是写起来确实很不爽。Guava也提供了toString()方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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(Object o)方法是java.lang.Comparable接口中的方法,当需要对某个类的对象进行排序时,该类需要实现Comparable接口的,必须重写public int compareTo(T o)方法。java规定,若a,b是两个对象,当a.compareTo(b)>0时,则a大于b,a.compareTo(b)<0时,a<b,即规定对象的比较大小的规则;
compare(Object o1, Object o2)方法是java.util.Comparator接口的方法,compare方法内主要靠定义的compareTo规定的对象大小关系规则来确定对象的大小。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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 ;
}
}
:
上面的compareTo方法,代码看上去并不是十分优雅,如果实体属性很多,数据类型丰富,代码可读性将会很差。在guava里, 对所有原始类型都提供了比较的工具函数来避免这个麻烦. 比如对Integer, 可以用Ints.compare()。利用guava的原始类型的compare,我们对上面的方法做一个简化,实现compare方法:
1
2
3
4
5
6
7
8
9
10
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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实现的compare和compareTo在代码可读性和性能上都有很大的提高。
下面是个综合应用的实例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
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
- 991998617
- 1163491205
========== toString ===========
Student { peida , 23 , 80 }
Student { aida , 23 , 36 }
Student { jerry , 24 , 90 }
Student { peida , 23 , 80 }
========== compareTo ===========
- 1