相等性判断的自动化 :: 沉思者

来源: BlogBus 原始链接: http://www.blogbus.com:80/blogbus/blog/diary.php?diaryid=543117 存档链接: https://web.archive.org/web/20050119215931id_/http://www.blogbus.com:80/blogbus/blog/diary.php?diaryid=543117


沉思者 我思故我在 <<<属性也应是接口 | 主页 | 为goto正名>>> 相等性判断的自动化 2004-12-15 在面向引用的OO编程语言中,相等性(Equality)操作有两种: 1、判断两个引用本身是否相等; 2、判断两个引用所引用的对象是否相等。 事实上,第二种操作是第一种操作的超集。因为,如果两个引用本身相等,则两个对象一定是相同的,当然也是相等的;但反之则不然。 对于第一种操作,执行起来非常简单。但第二种操作则相对复杂,需要程序员的指定或干涉。在Java中,具体表现为程序员需要为一个class编写equals方法。 两个对象本身的相等性应该具备下列性质: o Reflexive :对象应该等于自身,也就是第一种相等性操作; o Symmetric :对称性,即 a equals b 意味着 b euqals a; o Transitive :传递性,如果 a equals b,b equals c,则a equals c; o Consistent :一致性,在对象内容没有变化的情况下,两个对象之间进行多次比较时,结果应该是一致的。 对象本身的相等性判断,是高度模式化的操作。两个对象之间的相等性,在绝大多数情况下,其实是对象所包含的部分或全部属性之间的相等性。基于这种现实,我们应该把相等性判断的操作交给编译器去做。 首先,并非所有的class都需要进行对象内容的相等性判断,所以程序员可以指定一个class是否是compareable的,像这样: public compareable class Foo{ ... } 编译器只对声明了compareable的class生成内容相等性判断的代码。如果目标代码是Java的话,则只为声明为compareable的class生成equals方法。对于这种class的对象,就可以对其进行内容相等性判断,比如: Foo foo1 = new Foo(); Foo foo2 = new Foo(); if(foo1 == foo2) // 引用相等性判断,返回false ... if(foo1 equals foo2) // equals是关键字,用以进行内容相等性判断 ... 只有声明为compareable的类及其子类的对象,才能进行equals操作。编译器会检测这一点。 其次,在对两个compareable class的对象进行内容相等性判断时,并非所有属性都需要对比。所以应该允许程序员指定需要对比的属性。像这样: public compareable class Foo { private compareable int value; private compareable string name; private int dummy; ... } 在这个例子中,在对两个Foo对象进行相等性判断时,应该仅仅对value和name的值进行对比,由于dummy不是compareable的,所以无需对其进行相等性判断。 如果一个属性被指定为compareable的,则属性所属的类型必须是compareable的。否则编译器需要给出错误。对于基本类型,比如int, boolean, string, real等,默认就是compareable的,由于object是所有class的root class,所以object不是compareable的。比如: public compareable class Foo { private compareable int value; // valid private Exam exam; // Invalid, because class Exam isn't compareable. } public class Exam { ... } 对属性进行对比并不完全是对属性对象内容本身进行对比,有时候,如果属性是引用的话,只需要对引用进行对比就行了,对于这种情况,我们可以使用identical来声明。例如: public compareable class Foo { private compareable int value; // valid private identical Exam exam; // valid } public class Exam { ... } 对于identical属性所属的类型,不需要是compareable的。另外,对于非引用类型的基本类型,既可以使用compareable来修饰,也可以用identical来修饰,效果上是一致的。但二者不可以同时使用。 如果一个class的super class是被声明为compareable,则其默认就是compareable的;其相等性判断算法等同于super class。比如: public compareable class Base { public compareable int value = 10; } public class Derived extends Base{ } Base base = new Base(); Derived derived = new Derived(); if(base equals derived) // return true ... 但是,如果它想扩展super class的相等性判断算法,仍然必须明确声明为compareable的。比如: public compareable class Base { public compareable int value = 10; } // Invalid,因为它新指定了属性name为compareable的, // 所以Derived也必须被明确指定为compareable的。 public class Derived extends Base{ public compareable string name; ... } 还有一种情况,如果你想让一个类在内容相等性上区别于它的父类,也可以通过明确指定自身为compareable的来完成。例如: public compareable class Base { public compareable int value = 10; } public compareable class Derived extends Base{ } Base base = new Base(); Derived derived = new Derived(); if(base equals derived) // return false,既然Derived也有自己的compareable声明。 ... 对于集合性质的属性,如果被指定为compareable的,首先集合元素的类型应该是compareable的,其次,集合内元素的数量如果不相等,则两个集合肯定是不相等的。然后需要比较集合内的元素,此时,需要分为不同的情况来处理。 集合通过两种属性(ordered, unique)分为四类: 1、 Set (non-ordered, unique) 2、 OrderedSet (ordered, unique) 3、 Bag (non-ordered, non-unique) 4、 Sequence ( ordered, non-unique) 首先如果集合的类型如果不相同,则两个集合肯定是不相等的。 o 对于Set,我们通过判断一个Set中的元素是否包含在另外一个Set中来确定其相等性。 o 对于OrderedSet和Sequence,我们根据顺序依次判断两个集合中元素的相等性。 o 对于Bag的处理要相对复杂一些,我们首先对一个Bag进行过滤操作,把其重复的元素去掉,然后计算每一个非重复元素的数量,得到结果后,通过判断另外一个Bag中的非重复元素的数量是否匹配,得出相等性。 以上所描述的方法都是模式化的,这些都可以利用编译器进行自动代码生成。但有时候,相等性判断是非常特殊的操作。比如下面的Java代码: public class Foo { private int value; private string name; public boolean equals(Object object) { ... Foo foo = (Foo)object; if(value < 0) { if(foo.value > 0) return false; } else { if( foo.value != this.value) return false; } return foo.name.equals(this.age); } ... } 对于这些特殊情况,我们应该允许程序员来自定义特殊的相等性判断函数。可以像这样: public compareable class Foo { private int value; private compareable string name; equals { this.value < 0 implies rhs .value < 0; this.value > 0 implies this.value == rhs .value; } ... } 在我们的解决方案中,我们仍然可以指定那些无需特殊处理的属性为compareable的,对于特殊处理的条件,我们可以在equals block中,通过OCL boolean表达式来指定判断条件。在这些表达式都满足的情况下,再对指定为compareable的属性进行默认的相等性判断,以最后的结果来决定两个对象之间的相等性。 我们希望能够尽量避免让程序员编写模式化的操作,通过这些解决方案,应该可以很好的解决相等性模式代码生成的问题。 darwin_yuan 发表于 2004-12-15 14:14 引用Trackback(0) | 编辑 Comments 发表评论 最近更新 为goto正名 相等性判断的自动化 属性也应是接口 Delegate Class Package vs. Namespace Function Signature:新思维 Visibility: 两种观点 Java在Interface方面的缺陷 也谈对象的生命 Unlink操作