项目场景:实现抽象类接口图graph
(1)在实验中,在使用string类型作为参数实现concreteverticesgraph后,为了提高该adt的可复用性,需要将其中的string类转换为泛型参数L
(2)在实现graph的具体实现类时,需要重写equals和hashcode
(3)书写checkrep检查不变量保持为TRUE
问题描述
(1)在将其中的string类全部转换为泛型参数L之后,出现了报错情况,抛出了异常“java.lang.ClassCastException”
(2)equals()和hashCode()这两个方法属于Object类,而Object类是所有类的父类,因此所有的类都继承了这两个方法,所以需要在实现类中override这两个方法
(3)在书写每个类时需要书写checkup方法来保证RI的不变性,这里主要是针对immutable的类,减少因疏忽导致的表示泄露对程序带来的影响
原因分析:
(1)在遍历List<Vertex> vertices时,错误使用L key:的方式
(2)在object类中的equals方法:
public boolean equals(Object obj) {
return (this == obj);
}
它在其中比较的是两个对象指向的地址,而这种方法的适用率较低,“==”在比较基本数据类型例如int是适用的,因为对其赋值相同,它们即指向同一个内存地址,而对对象一般并不适用,因为由new出来的对象之间的内存地址是各不相同的,而我们想要到达这样一个目的:对象中内容(或者说rep)是相同时能够判断两个对象是满足equals返回TRUE的,所以有必要重写equals;
倘若该类中未涉及hash结构,则可不用重写hashcode部分(不予讨论)
(3)尽管在书写类的属性时习惯声明为private,防止在该类外对此属性的引用,导致其发生改变,但可能存在的表示泄露仍然会导致相同的结果,例如在构造函数时直接对外部传入参数的引用,又或者方法返回值的传递失误;
解决方案:
(1)采用两层循环嵌套:
for (Vertex<L> vertex : vertices) {
assert (vertex != null);
Map<L, Integer> sources = vertex.sources();
for (Map.Entry<L, Integer> entry : sources.entrySet()) {
(2)一般的重写模式例如:
@Override
public boolean equals(Object obj) {
Map<String, Integer> option = new HashMap<>();
option.putAll((Map<? extends String, ? extends Integer>) obj);
if(option.values().equals(this.options.values())) {
assertTrue(checkRep());
return true;
}
assertTrue(checkRep());
return false;
}
(3)利用assert标志符来检查不变量是否保持为TRUE(其中可以在checkrep内部采用assert xxx 检查一些属性的不变,也可以利用checkrep返回值为TRUE,在每个方法返回的最后以assert checkrep()的方式来检查)
第一种:
private void checkRep() {
for (L vertex : vertices)
assert (vertex != null);
for (Edge<L> edge : edges)
assert (edge != null);
}
第二种:
private boolean checkRep() {
Collection<Integer> v=new ArrayList<Integer>();
v=this.options.values();
if(v.size()!=options.size())
return false;
return true;
}
......
{assertTrue(checkrep());
return ...
}