考虑下面的程序,类Bigram 表示一个双字母组,考虑打印语句的输出结果
package thirtysix;
import java.util.HashSet;
import java.util.Set;
/**
* Bigram
* @author ZHAIYANMING
* @version 1.0
*
*/
public class Bigram {
private final char first;
private final char second;
public Bigram(char first, char second) {
this.first = first;
this.second = second;
}
public boolean equals(Bigram bigram) {
return bigram.first == first && bigram.second == second;
}
public int hashCode() {
return 31 * first + second;
}
public static void main(String[] args) {
Set<Bigram> bigrams = new HashSet<Bigram>();
for (int i = 0; i < 10; i++) {
for (char ch = 'a'; ch <= 'z'; ch++) {
bigrams.add(new Bigram(ch, ch));
}
}
System.out.println(bigrams.size());
}
}
为什么结果是这样?bigrams采用的集合类型是HashSet,为什么没有去掉重复的元素呢?
/**
* Adds the specified element to this set if it is not already present.
* More formally, adds the specified element <tt>e</tt> to this set if
* this set contains no element <tt>e2</tt> such that
* <tt>(e==null ? e2==null : e.equals(e2))</tt>.
* If this set already contains the element, the call leaves the set
* unchanged and returns <tt>false</tt>.
*
* @param e element to be added to this set
* @return <tt>true</tt> if this set did not already contain the specified
* element
*/
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
HashSet的add方法的底层说明:3~5行说明了add元素e的条件:set中不存在元素e2( e==null;e2==null;e.equals(e2) )。在元素都不为null的情况下,说明这行代码e.equals(e2) 与我们想要的结果不一样.
public boolean equals(Object obj) {
return (this == obj);
}
看一下Object类中的equals方法。发现代码中原本想要覆盖equals方法(发生覆盖的条件:“三同一不低”,子类和父类的方法名称,参数列表,返回类型必须完全相同,而且子类方法的访问修饰符的权限不能比父类低。),可是却重载了equals方法(发生重载的条件:方法名相同,参数列表(类型、个数、顺序)不同)。在进行这行代码( e.equals(e2) )时,就调用了Object类的equals方法,而Object类的equals方法是比较对象同一性的,即( e==e2 )。例子中:这行代码:bigrams.add(new Bigram(ch, ch)); 每次添加都会new Bigram(ch,ch),每一个Object都不一样,自然循环多少次,就add多少Object了。
随着java 1.5发行版本中增加注解,类库中也增加了几种注解类型。其中Override只能用在方法声明中,它表示被注解的方法声明覆盖了超类型中的一个声明。坚持使用这个注解,可以防止一大类非法错误。
@Override public boolean equals(Bigram bigram) {
return bigram.first == first && bigram.second == second;
}
如果插入这个注解,重新编译程序,编译器会产生一条错误:
The method equals(Bigram) of type Bigram must override or implement a supertype method
针对提示的错误咱们就可以修改相应的代码,这个equals方法测试对象的同一性,就像==:
@Override public boolean equals(Object o) {
if(!(o instanceof Bigram)) {
return false;
}
Bigram b = (Bigram) o;
return b.first == first && b.second == second;
}
总结:如果在你想要的每个方法声明中使用 Override 注解来覆盖超类声明 , 编译器就可以替你防止大量错误 。在具体的类中 , 不必标注你确信覆盖了抽象方法声明的方法(标注也可以)。