在
Effective Java 中,Joshua Bloch 提到,如果一个可实例化的类定义了 equals 方法。另有一个子类继承它,也定义了额外一些属性,并且 equals 方法中需要使用这些新定义的属性进行相等性判断。那么就不可能保证 equals 语义的正确。
相信看过
Effective Java 的人当年读到这里时都会觉得丧气。就好像完美的世界突然有了一个无法缝合的裂口。先不要完全丧失兴趣,看看下面的文章:
How to Write an Equality Method in Java
这篇主要由 Scala 的作者 Martin Odersky 执笔的文章中提到了一个有意思的方法。每个类在定义 equals 时,首先先判断 canEqual 能不能校验通过。canEqual 的作用就是限定:只有当被比较的对象是当前对象的子类或同类时才能通过。
class Point {
// 属性定义
...
boolean canEqual(Object other) {
return (other instanceof Point);
}
@Override boolean equals(Object other) {
if (other instanceof Point) {
Point that = (Point) other;
if (that.canEqual(this) && getX() == that.getX() && getY() == that.getY()) return true;
}
return false;
}
}
子类的定义与父类相似。
class ColoredPoint extends Point {
// 额外的属性定义
...
boolean canEqual(Object other) {
return (other instanceof ColoredPoint);
}
@Override boolean equals(Object other) {
if (other instanceof ColoredPoint) {
ColoredPoint that = (Point) other;
if (that.canEqual(this) && color.equals(that.color) && super.equals(that)) return true;
}
return false;
}
}
也就是说,在这样的约定下,如果拿一个重载了 canEqual 的子类的实例和一个父类实例比较肯定会返回 false。关于这篇文章,有兴趣的话可以看看相应的
讨论。
讨论主要集中在文章里的方法是否违背了 Liskov Substitution Principle (LSP),以及如果违背了那么这个问题有多严重上。看过下面的分析后大家也许会觉得这种讨论没有太多意义。
我个人推荐这种 canEqual 方法。我说“方法”而不说“解决方案”是因为我觉得 Odersky 所描述的 equals 实现与 Bloch 本来所期望的 equals 逻辑模型并不一致。想像 Odersky 文章中的例子。有一个类 - 点(Point),及其子类 - 有色点(ColoredPoint)。如果一个有色点实例,其坐标与一个普遍点坐标一样,又因为有色点“是”点,所以这两点应该“相等”。大家都期望这样一个结论是成立的,所以当看到 Bloch 的结论时会觉得面向对象有其固有的自相矛盾之处。但是这样一个结论却并不是天然成立的。一个没有颜色的点与一个有颜色的点能相等吗?有人会说,如果 ColoredPoint 里面的 color 属性是一个枚举,而且那个子类被实例化成 Color.UNSPECIFIED(未指定的颜色),那么这两个点逻辑上就应该相等了吧。我认为,如果 ColoredPoint.color 可以有这样一个属性值的话,那么 Point 类就应该被定义为抽象类。Point 类此时实例化没有意义。换句话说,如果 Point 类可以实例化,且其子类 ColoredPoint 也可以有一个“未指定的颜色”,而且两者都定义了 equals,那么出现这种情况我认为是设计失败。
再看看 LSP。LSP 说,任何可以使用父类实例的地方都可以使用子类实例代替。这里并不违反 LSP,因为如果一个地方可以这样调用:
Point p = new Point();
if (p.equals(...)) {
...
}
那么使用子类一样可以调用 equals。只不过,equals 在传入相同的参数时返回的结果可能会不一样。但是 LSP 并不约束必须返回一样的结果。而这正是多态的特征。
回到 Bloch 的论点上。现在赞同我的人可能会觉得 Bloch 的论点有问题。其实他说得很严谨,没有一丝问题。他的论点的前提是:可实例化的父类。也就是说无法针对非抽象类写出满足大家传统期望的子类。只不过,另人失望地,他在提出这个结论后没有给出对应的方法。相对来说,Odersky 理清了 Bloch 的逻辑模型。所以,在 Odersky 所发明的 Scala 中,canEqual 这个方法也被作为官方推荐的 equals 实现方法。
分享到:
相关推荐
Qt 信号在多层次对象间传递 多层嵌套类对象之间信号传递,可能是五层,或多层,子对象要发信号给第一层 ; QT信号量传递 QT信号量多层传递,QT信号量任意层传递,Qt信号量多层次对象间传递 博文:...
典型的多层结构z典型的多层结构z典型的多层结构z典型的多层结构z
多层钢结构体系及受力特点分析,李枫,,多层钢结构体系具有抗震性能好、建造速度快、自重轻、基础造价低、环保、可工业化生产等一系列优点,在我国建筑行业正得到越来越
某多层框架结构办公楼加层加固设计,魏常宝,郑建军,结合某多层办公楼加层工程实际,通过对原结构的计算分析,加层后原结构最大弹性层间位移角不满足规范要求,部分框架柱、部分框架
计算多层膜结构的透射率,用matlab进行仿真
简明易懂的阐述了多层架构开发模式的特点!推荐
多层钢结构施工方案().doc
多层胶接结构胶层均匀性的太赫兹时域表征方法.docx
耐高温多层隔热结构的高温稳定性研究,何飞,李明伟,以硅酸铝纤维纸和石英纤维网/布为基本隔热组元材料、以SiO2气凝胶和K2Ti6O13晶须为隔热填料、并以高温粘结剂为间隔层,采用粘结工艺�
多层砖混结构办公楼施工组织设计及对策.doc
多层结构C#博客(blog) vs2005 代码已经调试过,解压即可运行.
vs 2013 demo mvc4.0 razor views 多层结构 controller 和 view 分离 案例:AccountBase/MyTest/Test 注意文件 MyTestController.cs,RouteConfig.cs,Global.asax
基于多层薄膜结构色散性波分复用器的研究,温凯,黄永清,提出了一种基于光子晶体的波长选择复用器。从一维周期光子晶体出发,研究了其超棱镜效应并探讨了这种结构作为波长探测器的优缺点
多层框架结构结构设计.pptx
多层砖混结构办公楼工程施工组织设计及对策.doc
net.sf.json.JSONObject ==> Java对象
.NET多层体系结构设计,层次分明,适合架构师及开发者参考
多层框架办公楼结构全套施工图,结构设计的好参考。
复合多层结构的隔声研究,陈卫松,邱小军,应用转移矩阵的方法,就平面声波垂直入射的情况,对具有周期结构的无限大多层板的隔声特性进行了理论分析,并对结构不同的多层板的隔
多层板阻抗层压结构多层板阻抗层压结构多层板阻抗层压结构