相等断定—————————equals [Obj]

本文详细讲解了Java中equals方法的重写原则与实践,包括自反性、对称性、传递性和一致性等基本要求,以及如何正确重写equals方法以比较对象的内容而非地址。

 1. 重写equals方法的要求

public boolean equals(Object obj)

指明与参数obj指定的对象是否相等,相等返回true,否则返回false.

关于equals方法的实现,java API文档中标明了以下几点要求。

·自反性。 对于任何非null的引用值 x,x.equals(x)应返回true。

·对称性。 对于任何非null的引用值 x与y,当且仅当y.equals(x)返回true时,x.equals(y)才应返回true。

·传递性。 对于任何非null的引用值 x,y与z,如果 x.equals(y)返回true,并且 y.equals(z)返回true,那么

x.equals(z)也应返回true。

·一致性。 对于任何非空引用值x与y,假设对象上equals比较的信息没有被修改,则多次调用x.equals(y)始终返回true或始终返回false。

·对于任何非空引用值 x,x.equals(null)应返回false.

了解equals的规则后,现在思考这样一个问题:equals方法比较的是什么?

 

有些初学者受String类的影响,认为equals方法比较的是对象的内容,而“==”比较的是对象的地址,即两个引用是否指向同一个对象所在的内存地址,这是不对的。

 

之所以String类的equals方法比较的是对象的内容(即字符串的内容)而非地址,是因为String类重写了equals方法,而如果默认的equals方法来讲(也就是从Object类继承的equals方法),

其比较的也是对象的地址,即默认的equals方法与“==”的比较方式是一样的。

请看Object类中equals的方法的实现:

 

实现很简单,实际上就是使用“==”来判断obj与this是否指向相同的对象。

下例中说明了equals方法的比较

 

 

 

程序运行结果如下:

 

 

String 类因为重写了equals方法,所以其比较的是对象的内容,而不是对象的地址,而str1与str2指向的字符串是相等的,因此,equals比较的结果为true.

尽管两个Box的引用box1和box2指向的对象中对应的成员变量都是相等的,但是Box类并没有重写equals方法,而使用的是从Object类中继承的equals方法(比较的是对象的地址),因此返回false。

当然,如果执行box2=box1,则box1和box2两个引用同时指向了以前box1指向的对象,自然再次调用equals时返回true.

通常,我们应该重写equals方法,就像String类那样,使得equals方法比较的是对象的内容而不是对象的地址。

可以这样重写Box类的equals方法:

 

 

对于非Box类的对象,应该认为是不相等的,这里使用instanceof来判断引用obj所指向对象的类型。

然后比较两个对象的成员变量,如果相等则返回true,否则返回false。

equals方法中并没有对obj为null的情况做判断,因为该判读已经隐藏在instanceof中了,如果:

则:

 

恒为false。

 

2.equals方法分析

如果只用在上面的程序中,equals方法可以运行正常,不过,如果期望子类与父类的对象之间可以进行混合比较时,在equals方法中仅仅使用instanceof运算符来判断还不够,下例中介绍这种情况。

 

 

程序中定义了Line(线)、Curve(曲线),并且在各自的类中重写了equals方法,程序期望实现以下要求。

(1)当相同类型的对象比较时;

·如果两条线(Line)的长度相等,则两条线相等,否则不相等;

·如果两条曲线(Curve)的长度相等,则两条曲线相等,否则不相等。

(2)当父类与子类(这里指线与曲线)进行混合比较时;

·如果线与曲线的长度相等,则相等,否则不相等。

(3)对于其他类型的比较,认为不相等。

 

程序实现:

 

 

程序运行结果:

 

 

在equals中使用instanceof运算符时,出现了一点小问题,例如在Line类中:

如果该表达式值为true,那么只能说明可以将obj转换成Line类型,却不能说obj引用一定指向Line类的对象,

当obj指向Curve类的对象时( Line的子类),该表达式值同样为true,

例如:

两次输出true。因此,使用instanceof运算符并不能判断出某引用所指向对象的准确类型,而只能得出对象是instanceof运算符右侧操作数的类型或右侧操作数的子类型。

因此一旦进行父类与子类的比较时,就会违背equals方法对称性原则。

所以,在equals方法中,当使用instanceof对参数类型做判断时,如果参数类型并非当前类的类型,不能直接返回 false,而是应该递归地调用父类的equals方法继续做相等的判断,例如Curve类的equals方法,应该这样修改:

 

 

于是上面例子的修正:

 

 

运行结果:

 

 

 

这样修改后,equals方法也就符合对称性的规则了,并且当子类对象与父类对象混合比较时,也遵守传递性。但是,这种方式依然不是完美的,这种传递性只是限定在子类没有新增成员变量或者子类中新增的成员变量没有作用在equals方法的比较中。一旦子类的equals方法使用了新增的成员变量作为比较条件,则当进行父类与子类对象的混合比较时,equals方法就会变得很脆弱了。

 

例:

 

 

运行结果:

 

从而得知,在equals方法中使用了子类新增的成员作为比较,当父类与子类对象进行混合比较时,就会与传递性相矛盾,而这种情况并不能通过修改equals方法解决,如果可能,当子类中新增成员,而该成员又作为equals方法的比较条件时,最好避免这样的混合比较。

 

### Python 函数用于计算思维训练 #### 方法和示例 Python 提供了一种强大的方式来进行计算思维训练,特别是通过函数的设计与实现。函数不仅有助于代码的模块化设计,还促进了逻辑思考能力的发展。 #### 问题分解 一个问题可以被拆解成更小的部分以便更容易解决。例如,在处理复杂的数据集时,可以通过编写多个小型专用功能来逐步解决问题[^1]。下面是一个具体的例子: 假设有一个需求是要找出列表中所有偶数的位置索引。这个问题可以通过构建辅助函数 `is_even` 来判断单个数字是否为偶数,再利用另一个遍历整个列表并收集符合条件项位置的功能完成最终目标。 ```python def is_even(number): """Check if a number is even.""" return number % 2 == 0 def find_indices_of_evens(lst): """Find indices of all even numbers in the list.""" result = [] for index, value in enumerate(lst): if is_even(value): result.append(index) return result ``` 这段代码展示了如何将大任务分割成易于管理和理解的小部分——这正是计算思维的核心之一。 #### 自定义函数的应用 除了上述提到的问题分解技巧外,还可以创建自定义函数以满足特定的需求。比如想要统计一段文字里单词的数量,就可以按照以下步骤操作[^3]: 1. 定义一个新的函数接受字符串作为输入; 2. 使用内置 split() 方法把句子切分成词组; 3. 计算得到的结果集合大小即代表总共有多少个不同词语; 这里给出完整的解决方案如下所示: ```python def word_count(text): words = text.split() unique_words = set(words) return len(unique_words) example_text = "This is an example sentence with some repeated words like this and that" print(word_count(example_text)) ``` 此程序片段说明了怎样运用自定义函数有效地解决了实际生活中的一个小挑战。 #### 实现加法运算器 为了进一步展示 Python 函数对于培养计算思维方式的重要性,考虑这样一个场景:开发一个简单的计算器应用程序,其中一部分负责执行两数值相加的任务。该过程涉及到了基本算法的理解以及良好的编码习惯养成等方面[^4]。 ```python def add_numbers(a, b): """Add two given numbers together""" sum_result = a + b return sum_result num1 = float(input("Enter first number: ")) num2 = float(input("Enter second number: ")) result = add_numbers(num1, num2) print(f"The addition of {num1} and {num2} equals to {result}") ``` 以上实例强调了函数在简化重复工作流程方面的作用,同时也体现了结构清晰、易于调试的优点。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值